default is actually stable stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 21 Jul 2011 08:11:07 +0200
branchstable
changeset 646 e1cc9656bb4c
parent 639 1a9f3c52176b (current diff)
parent 645 bc901a7460d1 (diff)
child 647 6afe7acaf314
default is actually stable
--- a/ChangeLog	Tue Jul 05 12:49:38 2011 +0200
+++ b/ChangeLog	Thu Jul 21 08:11:07 2011 +0200
@@ -2,10 +2,20 @@
 =================
 
 --
-    * remove_group_var renamed into remove_group_term and fixed implementation
+    * #70264: remove_group_var renamed into remove_group_term and fixed
+      implementation
+
+    * #70416: rql annotator add 'having' list into variable's stinfo, and
+      properly update variable graph
 
-    * rql annotator add 'having' list into variable's stinfo, and
-      properly update variable graph
+    * #71131: as_string doesn't  propagate encoding/kwargs to subqueries
+
+    * #71132: column alias scope should be handled as variable scope, not bound
+      to subquery
+
+    * #71157: bad analyze when using functions
+
+    * Select.replace must properly reset old node's parent attribute
 
     * new undo_modification context manager on select nodes
 
--- a/analyze.py	Tue Jul 05 12:49:38 2011 +0200
+++ b/analyze.py	Thu Jul 21 08:11:07 2011 +0200
@@ -493,45 +493,33 @@
             if not isinstance(lhs, nodes.VariableRef) or rhs.type is None:
                 return True
             self._extract_constraint(constraints, lhs.name, rhs, rschema.subjects)
-        else:
-            if not isinstance(lhs, nodes.VariableRef):
-                # XXX: check relation is valid
-                return True
+        elif not isinstance(lhs, nodes.VariableRef):
+            # XXX: check relation is valid
+            return True
+        elif isinstance(rhs, nodes.VariableRef):
             lhsvar = lhs.name
-            rhsvars = []
-            samevar = False
-            if not isinstance(rhs, nodes.MathExpression):
-                # rhs type is the result of the math expression, not of
-                # individual variables, so don't add constraints on rhs
-                # variables
-                for v in rhs.iget_nodes(nodes.VariableRef):
-                    if v.name == lhsvar:
-                        samevar = True
-                    else:
-                        rhsvars.append(v.name)
+            rhsvar = rhs.name
             lhsdomain = constraints.domains[lhsvar]
-            if rhsvars:
-                s2 = '=='.join(rhsvars)
-                # filter according to domain necessary for column aliases
-                rhsdomain = constraints.domains[rhsvars[0]]
-                res = []
-                for fromtype, totypes in rschema.associations():
-                    if not fromtype in lhsdomain:
-                        continue
-                    ptypes = [str(t) for t in totypes if t in rhsdomain]
-                    res.append( [ ( [lhsvar], [str(fromtype)]), (rhsvars, ptypes) ] )
-                constraints.or_and( res )
-            else:
-                ptypes = [str(subj) for subj in rschema.subjects()
-                          if subj in lhsdomain]
-                constraints.var_has_types( lhsvar, ptypes )
-            if samevar:
-                res = []
-                for fromtype, totypes in rschema.associations():
-                    if not (fromtype in totypes and fromtype in lhsdomain):
-                        continue
-                    res.append(str(fromtype))
+            # filter according to domain necessary for column aliases
+            rhsdomain = constraints.domains[rhsvar]
+            res = []
+            for fromtype, totypes in rschema.associations():
+                if not fromtype in lhsdomain:
+                    continue
+                ptypes = [str(t) for t in totypes if t in rhsdomain]
+                res.append( [ ([lhsvar], [str(fromtype)]),
+                              ([rhsvar], ptypes) ] )
+            constraints.or_and(res)
+            if rhsvar == lhsvar:
+                res = [str(fromtype) for fromtype, totypes in rschema.associations()
+                       if (fromtype in totypes and fromtype in lhsdomain)]
                 constraints.var_has_types( lhsvar, res )
+        else:
+            # XXX consider rhs.get_type?
+            lhsdomain = constraints.domains[lhs.name]
+            ptypes = [str(subj) for subj in rschema.subjects()
+                      if subj in lhsdomain]
+            constraints.var_has_types( lhs.name, ptypes )
         return True
 
     def visit_type_restriction(self, relation, constraints):
--- a/nodes.py	Tue Jul 05 12:49:38 2011 +0200
+++ b/nodes.py	Thu Jul 21 08:11:07 2011 +0200
@@ -195,6 +195,7 @@
         """builds a restriction node to express : variable is etype"""
         return self.add_constant_restriction(var, 'is', etype, 'etype')
 
+
 # base RQL nodes ##############################################################
 
 class SubQuery(BaseNode):
@@ -224,7 +225,7 @@
 
     def as_string(self, encoding=None, kwargs=None):
         return '%s BEING (%s)' % (','.join(v.name for v in self.aliases),
-                                  self.query.as_string())
+                                  self.query.as_string(encoding, kwargs))
     def __repr__(self):
         return '%s BEING (%s)' % (','.join(repr(v) for v in self.aliases),
                                   repr(self.query))
@@ -825,7 +826,7 @@
 ###############################################################################
 
 class Referenceable(object):
-    __slots__ = ('name', 'stinfo')
+    __slots__ = ('name', 'stinfo', 'stmt')
 
     def __init__(self, name):
         self.name = name.strip().encode()
@@ -834,6 +835,12 @@
             # link to VariableReference objects in the syntax tree
             'references': set(),
             }
+        # reference to the selection
+        self.stmt = None
+
+    @property
+    def schema(self):
+        return self.stmt.root.schema
 
     def init_copy(self, old):
         # should copy variable's possibletypes on copy
@@ -862,6 +869,7 @@
 
     def prepare_annotation(self):
         self.stinfo.update({
+            'scope': None,
             # relations where this variable is used on the lhs/rhs
             'relations': set(),
             'rhsrelations': set(),
@@ -882,6 +890,18 @@
         for key in ('optrelations', 'blocsimplification', 'ftirels'):
             self.stinfo.pop(key, None)
 
+    def _set_scope(self, key, scopenode):
+        if scopenode is self.stmt or self.stinfo[key] is None:
+            self.stinfo[key] = scopenode
+        elif not (self.stinfo[key] is self.stmt or scopenode is self.stinfo[key]):
+            self.stinfo[key] = common_parent(self.stinfo[key], scopenode).scope
+
+    def set_scope(self, scopenode):
+        self._set_scope('scope', scopenode)
+    def get_scope(self):
+        return self.stinfo['scope']
+    scope = property(get_scope, set_scope)
+
     def add_optional_relation(self, relation):
         try:
             self.stinfo['optrelations'].add(relation)
@@ -985,10 +1005,6 @@
     def __repr__(self):
         return 'alias %s(%#X)' % (self.name, id(self))
 
-    @property
-    def schema(self):
-        return self.query.root.schema
-
     def get_type(self, solution=None, kwargs=None):
         """return entity type of this object, 'Any' if not found"""
         vtype = super(ColumnAlias, self).get_type(solution, kwargs)
@@ -1013,12 +1029,6 @@
                 return ', '.join(sorted(vtype for vtype in vtypes))
         return vtype
 
-    def set_scope(self, scopenode):
-        pass
-    def get_scope(self):
-        return self.query
-    scope = property(get_scope, set_scope)
-
 
 class Variable(Referenceable):
     """
@@ -1027,37 +1037,11 @@
 
     collects information about a variable use in a syntax tree
     """
-    __slots__ = ('stmt',
-                 '_q_invariant', '_q_sql', '_q_sqltable') # XXX ginco specific
-
-    def __init__(self, name):
-        super(Variable, self).__init__(name)
-        # reference to the selection
-        self.stmt = None
+    __slots__ = ('_q_invariant', '_q_sql', '_q_sqltable') # XXX ginco specific
 
     def __repr__(self):
         return '%s(%#X)' % (self.name, id(self))
 
-    @property
-    def schema(self):
-        return self.stmt.root.schema
-
-    def prepare_annotation(self):
-        super(Variable, self).prepare_annotation()
-        self.stinfo['scope'] = None
-
-    def _set_scope(self, key, scopenode):
-        if scopenode is self.stmt or self.stinfo[key] is None:
-            self.stinfo[key] = scopenode
-        elif not (self.stinfo[key] is self.stmt or scopenode is self.stinfo[key]):
-            self.stinfo[key] = common_parent(self.stinfo[key], scopenode).scope
-
-    def set_scope(self, scopenode):
-        self._set_scope('scope', scopenode)
-    def get_scope(self):
-        return self.stinfo['scope']
-    scope = property(get_scope, set_scope)
-
     def valuable_references(self):
         """return the number of "valuable" references :
         references is in selection or in a non type (is) relations
--- a/stmts.py	Tue Jul 05 12:49:38 2011 +0200
+++ b/stmts.py	Thu Jul 21 08:11:07 2011 +0200
@@ -484,7 +484,7 @@
             s.append('WHERE ' + as_string(self.where))
         if self.having:
             s.append('HAVING ' + ','.join(as_string(term)
-                                           for term in self.having))
+                                          for term in self.having))
         if self.with_:
             s.append('WITH ' + ','.join(as_string(term)
                                         for term in self.with_))
@@ -618,6 +618,7 @@
             return self.aliases[name]
         if colnum is not None: # take care, may be 0
             self.aliases[name] = calias = nodes.ColumnAlias(name, colnum)
+            calias.stmt = self
             # alias may already have been used as a regular variable, replace it
             if name in self.defined_vars:
                 var = self.defined_vars.pop(name)
@@ -708,8 +709,7 @@
             raise Exception('duh XXX %s' % oldnode)
         # XXX no undo/reference support 'by design' (eg breaks things if you add
         # it...)
-        # XXX resetting oldnode parent cause pb with cw.test_views (w/ facets)
-        #oldnode.parent = None
+        oldnode.parent = None
         newnode.parent = self
         return oldnode, self, None
 
--- a/test/unittest_analyze.py	Tue Jul 05 12:49:38 2011 +0200
+++ b/test/unittest_analyze.py	Thu Jul 21 08:11:07 2011 +0200
@@ -531,7 +531,7 @@
                                 {'P': 'Student', 'S': 'Company', 'N': 'Int'}])
 
 
-    def test_nongrer_not_u_ownedby_u(self):
+    def test_nonregr_not_u_ownedby_u(self):
         node = self.helper.parse('Any U WHERE NOT U owned_by U')
         self.helper.compute_solutions(node, debug=DEBUG)
         sols = sorted(node.children[0].solutions)
--- a/test/unittest_stcheck.py	Tue Jul 05 12:49:38 2011 +0200
+++ b/test/unittest_stcheck.py	Thu Jul 21 08:11:07 2011 +0200
@@ -314,6 +314,8 @@
         C = rqlst.with_[0].query.children[0].defined_vars['C']
         self.failUnless(C.scope is rqlst.with_[0].query.children[0], C.scope)
         self.assertEqual(len(C.stinfo['relations']), 2)
+        X = rqlst.get_variable('X')
+        self.failUnless(X.scope is rqlst, X.scope)
 
     def test_no_attr_var_if_uid_rel(self):
         with self.assertRaises(BadRQLQuery) as cm: