diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
index a56ab5c..2f3b7a7 100644
--- a/django/db/models/query_utils.py
+++ b/django/db/models/query_utils.py
@@ -47,8 +47,15 @@ class Q(tree.Node):
         if not isinstance(other, Q):
             raise TypeError(other)
         obj = type(self)()
-        obj.add(self, conn)
-        obj.add(other, conn)
+        obj.connector = conn
+        if len(self) == 1 and not self.negated:
+            obj.add(self.children[0], conn)
+        else:
+            obj.add(self, conn)
+        if len(other) == 1 and not other.negated:
+            obj.add(other.children[0], conn)
+        else:
+            obj.add(other, conn)
         return obj
 
     def __or__(self, other):
@@ -58,8 +65,7 @@ class Q(tree.Node):
         return self._combine(other, self.AND)
 
     def __invert__(self):
-        obj = type(self)()
-        obj.add(self, self.AND)
+        obj = self.clone()
         obj.negate()
         return obj
 
diff --git a/django/db/models/sql/aggregates.py b/django/db/models/sql/aggregates.py
index 207bc0c..ee43808 100644
--- a/django/db/models/sql/aggregates.py
+++ b/django/db/models/sql/aggregates.py
@@ -69,6 +69,11 @@ class Aggregate(object):
 
         self.field = tmp
 
+    def clone(self):
+        clone = copy.copy(self)
+        clone.col = self.col[:]
+        return clone
+
     def relabel_aliases(self, change_map):
         if isinstance(self.col, (list, tuple)):
             self.col = (change_map.get(self.col[0], self.col[0]), self.col[1])
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index 6bf7de2..4eb69cb 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -5,7 +5,7 @@ from django.db import transaction
 from django.db.backends.util import truncate_name
 from django.db.models.query_utils import select_related_descend
 from django.db.models.sql.constants import *
-from django.db.models.sql.datastructures import EmptyResultSet
+from django.db.models.sql.datastructures import EmptyResultSet, FullResultSet
 from django.db.models.sql.expressions import SQLEvaluator
 from django.db.models.sql.query import get_proxied_model, get_order_dir, Query
 from django.db.utils import DatabaseError
@@ -68,7 +68,6 @@ class SQLCompiler(object):
         from_, f_params = self.get_from_clause()
 
         qn = self.quote_name_unless_alias
-
         where, w_params = self.query.where.as_sql(qn=qn, connection=self.connection)
         having, h_params = self.query.having.as_sql(qn=qn, connection=self.connection)
         params = []
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 61fd2be..73b597d 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -20,8 +20,8 @@ from django.db.models.sql import aggregates as base_aggregates_module
 from django.db.models.sql.constants import *
 from django.db.models.sql.datastructures import EmptyResultSet, Empty, MultiJoin
 from django.db.models.sql.expressions import SQLEvaluator
-from django.db.models.sql.where import (WhereNode, Constraint, EverythingNode,
-    ExtraWhere, AND, OR)
+from django.db.models.sql.where import (WhereNode, Constraint, ExtraWhere,
+    AND, OR)
 from django.core.exceptions import FieldError
 
 __all__ = ['Query', 'RawQuery']
@@ -254,13 +254,13 @@ class Query(object):
         obj.dupe_avoidance = self.dupe_avoidance.copy()
         obj.select = self.select[:]
         obj.tables = self.tables[:]
-        obj.where = copy.deepcopy(self.where, memo=memo)
+        obj.where = self.where.clone()
         obj.where_class = self.where_class
         if self.group_by is None:
             obj.group_by = None
         else:
             obj.group_by = self.group_by[:]
-        obj.having = copy.deepcopy(self.having, memo=memo)
+        obj.having = self.having.clone()
         obj.order_by = self.order_by[:]
         obj.low_mark, obj.high_mark = self.low_mark, self.high_mark
         obj.distinct = self.distinct
@@ -268,7 +268,10 @@ class Query(object):
         obj.select_for_update_nowait = self.select_for_update_nowait
         obj.select_related = self.select_related
         obj.related_select_cols = []
-        obj.aggregates = copy.deepcopy(self.aggregates, memo=memo)
+        if self.aggregates:
+            obj.aggregates = copy.deepcopy(self.aggregates, memo=memo)
+        else:
+            obj.aggregates = SortedDict()
         if self.aggregate_select_mask is None:
             obj.aggregate_select_mask = None
         else:
@@ -291,7 +294,7 @@ class Query(object):
             obj._extra_select_cache = self._extra_select_cache.copy()
         obj.extra_tables = self.extra_tables
         obj.extra_order_by = self.extra_order_by
-        obj.deferred_loading = copy.deepcopy(self.deferred_loading, memo=memo)
+        obj.deferred_loading = self.deferred_loading[0].copy(), self.deferred_loading[1]
         if self.filter_is_sticky and self.used_aliases:
             obj.used_aliases = self.used_aliases.copy()
         else:
@@ -499,23 +502,18 @@ class Query(object):
                 if self.alias_refcount.get(alias) or rhs.alias_refcount.get(alias):
                     self.promote_alias(alias, True)
 
-        # Now relabel a copy of the rhs where-clause and add it to the current
-        # one.
-        if rhs.where:
-            w = copy.deepcopy(rhs.where)
-            w.relabel_aliases(change_map)
-            if not self.where:
-                # Since 'self' matches everything, add an explicit "include
-                # everything" where-constraint so that connections between the
-                # where clauses won't exclude valid results.
-                self.where.add(EverythingNode(), AND)
-        elif self.where:
-            # rhs has an empty where clause.
-            w = self.where_class()
-            w.add(EverythingNode(), AND)
+        if connector == OR and (not self.where or not rhs.where):
+            # One of the two sides matches everything and the connector is OR.
+            # This means the new where condition must match everything.
+            self.where = self.where_class()
         else:
-            w = self.where_class()
-        self.where.add(w, connector)
+            rhs_where = rhs.where.clone()
+            rhs_where.relabel_aliases(change_map)
+            self.where = self.where_class([self.where, rhs_where], connector)
+            # the root node's connector must always be AND
+            if self.where.connector == OR:
+                self.where = self.where_class([self.where])
+            self.where.prune_tree(recurse=True)
 
         # Selection columns and extra extensions are those provided by 'rhs'.
         self.select = []
@@ -1071,11 +1069,7 @@ class Query(object):
 
         for alias, aggregate in self.aggregates.items():
             if alias in (parts[0], LOOKUP_SEP.join(parts)):
-                entry = self.where_class()
-                entry.add((aggregate, lookup_type, value), AND)
-                if negate:
-                    entry.negate()
-                self.having.add(entry, connector)
+                self.having.add((aggregate, lookup_type, value), connector)
                 return
 
         opts = self.get_meta()
@@ -1183,56 +1177,90 @@ class Query(object):
                 self.add_filter(filter, negate=negate, can_reuse=can_reuse,
                         process_extras=False)
 
-    def add_q(self, q_object, used_aliases=None, force_having=False):
+    def add_q(self, q_object, force_having=False):
         """
         Adds a Q-object to the current filter.
 
         Can also be used to add anything that has an 'add_to_query()' method.
+
+        In case add_to_query path is not executed, this method's main purpose
+        is to walk the q_object's internal nodes and manage the state of the
+        self.where / self.having trees. Leaf nodes will be handled by
+        add_filter.
+
+        The self.where / self.having trees are managed by pushing new nodes
+        to self.where / self.having. This way self.where / self.having is
+        always at the right node when add_filter adds items to them.
+
+        We need to start a new subtree when:
+           - The connector of the q_object is different than the connector of
+             the where / having tree.
+           - The q_object is negated.
+
+        After call of this function with q_object=~Q(pk=1)&~Q(Q(pk=3)|Q(pk=2))
+        we should have the following tree:
+                      AND
+                     /   \
+                    NOT  NOT
+                     |     \
+                    pk=1   OR
+                          /  \
+                        pk=3 pk=2
+
+        This method will call recursively itself for those childrens of the
+        q_object which are Q-objs, and call add_filter for the leaf nodes.
         """
-        if used_aliases is None:
-            used_aliases = self.used_aliases
+
+        # Complex custom objects are responsible for adding themselves.
         if hasattr(q_object, 'add_to_query'):
-            # Complex custom objects are responsible for adding themselves.
-            q_object.add_to_query(self, used_aliases)
-        else:
-            if self.where and q_object.connector != AND and len(q_object) > 1:
-                self.where.start_subtree(AND)
-                subtree = True
+            q_object.add_to_query(self, self.used_aliases)
+            return
+
+        # We need to check upfront if this whole tree should be placed in
+        # the query's having clause or not. The reason is we can't have
+        # one part of ORed clause in having and the other in where. Once set,
+        # force_having can't be changed later on.
+        if not force_having and q_object.connector == OR:
+            force_having = self.need_force_having(q_object)
+
+        # Start subtrees for both having and where if needed. At the end we
+        # check if anything got added into the subtrees. If not, prune em.
+        where_subtree = False
+        having_subtree = False
+        connector = q_object.connector
+        if self.having.connector <> connector or q_object.negated:
+            self.having = self.having.subtree(q_object.connector)
+            having_subtree = True
+        if self.where.connector <> connector or q_object.negated:
+            self.where = self.where.subtree(q_object.connector)
+            where_subtree = True
+        if q_object.negated:
+            self.where.negate()
+            self.having.negate()
+
+        # Aliases that were newly added or not used at all need to
+        # be promoted to outer joins if they are nullable relations.
+        # (they shouldn't turn the whole conditional into the empty
+        # set just because they don't match anything). Take the
+        # before snapshot of the aliases.
+        if connector == OR:
+            refcounts_before = self.alias_refcount.copy()
+
+        for child in q_object.children:
+            if isinstance(child, Node):
+                self.add_q(child, force_having=force_having)
             else:
-                subtree = False
-            connector = AND
-            if q_object.connector == OR and not force_having:
-                force_having = self.need_force_having(q_object)
-            for child in q_object.children:
-                if connector == OR:
-                    refcounts_before = self.alias_refcount.copy()
-                if force_having:
-                    self.having.start_subtree(connector)
-                else:
-                    self.where.start_subtree(connector)
-                if isinstance(child, Node):
-                    self.add_q(child, used_aliases, force_having=force_having)
-                else:
-                    self.add_filter(child, connector, q_object.negated,
-                            can_reuse=used_aliases, force_having=force_having)
-                if force_having:
-                    self.having.end_subtree()
-                else:
-                    self.where.end_subtree()
-
-                if connector == OR:
-                    # Aliases that were newly added or not used at all need to
-                    # be promoted to outer joins if they are nullable relations.
-                    # (they shouldn't turn the whole conditional into the empty
-                    # set just because they don't match anything).
-                    self.promote_unused_aliases(refcounts_before, used_aliases)
-                connector = q_object.connector
-            if q_object.negated:
-                self.where.negate()
-            if subtree:
-                self.where.end_subtree()
-        if self.filter_is_sticky:
-            self.used_aliases = used_aliases
+                self.add_filter(child, connector, q_object.negated,
+                        can_reuse=self.used_aliases, force_having=force_having)
+
+        if connector == OR:
+            self.promote_unused_aliases(refcounts_before, self.used_aliases)
+        if having_subtree:
+            self.having = self.having.parent
+            self.having.prune_tree()
+        if where_subtree:
+            self.where = self.where.parent
+            self.where.prune_tree()
 
     def setup_joins(self, names, opts, alias, dupe_multis, allow_many=True,
             allow_explicit_fk=False, can_reuse=None, negate=False,
diff --git a/django/db/models/sql/where.py b/django/db/models/sql/where.py
index 3e9dbf0..b2b5b3c 100644
--- a/django/db/models/sql/where.py
+++ b/django/db/models/sql/where.py
@@ -64,27 +64,37 @@ class WhereNode(tree.Node):
 
         if hasattr(obj, "prepare"):
             value = obj.prepare(lookup_type, value)
-            super(WhereNode, self).add((obj, lookup_type, annotation, value),
-                connector)
-            return
 
         super(WhereNode, self).add((obj, lookup_type, annotation, value),
                 connector)
 
     def as_sql(self, qn, connection):
         """
-        Returns the SQL version of the where clause and the value to be
-        substituted in. Returns None, None if this node is empty.
+        Returns the SQL version of the where clause and the values to be
+        substituted in.
+
+        If the tree evaluates to always true, then the function will return
+        ("", []). If the function evaluates to always false, EmptyResultSet
+        will be risen.
+        """
+        try:
+            return self._as_sql(qn, connection)
+        except FullResultSet:
+            return "", []
 
-        If 'node' is provided, that is the root of the SQL generation
-        (generally not needed except by the internal implementation for
-        recursion).
+    def _as_sql(self, qn, connection):
+        """
+        Internal helper.
         """
         if not self.children:
-            return None, []
+            return '', []
         result = []
         result_params = []
-        empty = True
+        # Track the amount of EmptyResultSets and FullResultSets this node
+        # has. These + self.connector and self.negated are used to check
+        # if this node matches nothing or matches everything.
+        empty_vals = 0
+        full_vals = 0
         for child in self.children:
             try:
                 if hasattr(child, 'as_sql'):
@@ -92,30 +102,23 @@ class WhereNode(tree.Node):
                 else:
                     # A leaf node in the tree.
                     sql, params = self.make_atom(child, qn, connection)
-
-            except EmptyResultSet:
-                if self.connector == AND and not self.negated:
-                    # We can bail out early in this particular case (only).
-                    raise
-                elif self.negated:
-                    empty = False
-                continue
-            except FullResultSet:
-                if self.connector == OR:
-                    if self.negated:
-                        empty = True
-                        break
-                    # We match everything. No need for any constraints.
-                    return '', []
-                if self.negated:
-                    empty = True
-                continue
-
-            empty = False
-            if sql:
                 result.append(sql)
                 result_params.extend(params)
-        if empty:
+            except EmptyResultSet:
+                empty_vals += 1
+            except FullResultSet:
+                full_vals += 1
+
+        if self.negated:
+            full_vals, empty_vals = empty_vals, full_vals
+
+        if full_vals > 0 and self.connector == OR:
+            raise FullResultSet
+        if full_vals == len(self) and self.connector == AND:
+            raise FullResultSet
+        if empty_vals == len(self) and self.connector == OR:
+            raise EmptyResultSet
+        if empty_vals > 0 and self.connector == AND:
             raise EmptyResultSet
 
         conn = ' %s ' % self.connector
@@ -249,27 +252,6 @@ class WhereNode(tree.Node):
                 if hasattr(child[3], 'relabel_aliases'):
                     child[3].relabel_aliases(change_map)
 
-class EverythingNode(object):
-    """
-    A node that matches everything.
-    """
-
-    def as_sql(self, qn=None, connection=None):
-        raise FullResultSet
-
-    def relabel_aliases(self, change_map, node=None):
-        return
-
-class NothingNode(object):
-    """
-    A node that matches nothing.
-    """
-    def as_sql(self, qn=None, connection=None):
-        raise EmptyResultSet
-
-    def relabel_aliases(self, change_map, node=None):
-        return
-
 class ExtraWhere(object):
     def __init__(self, sqls, params):
         self.sqls = sqls
@@ -278,6 +260,9 @@ class ExtraWhere(object):
     def as_sql(self, qn=None, connection=None):
         return " AND ".join(self.sqls), tuple(self.params or ())
 
+    def clone(self):
+        return self
+
 class Constraint(object):
     """
     An object that can be passed to WhereNode.add() and knows how to
@@ -342,3 +327,6 @@ class Constraint(object):
     def relabel_aliases(self, change_map):
         if self.alias in change_map:
             self.alias = change_map[self.alias]
+
+    def clone(self):
+        return Constraint(self.alias, self.col, self.field)
diff --git a/django/utils/tree.py b/django/utils/tree.py
index 36b5977..4be04d5 100644
--- a/django/utils/tree.py
+++ b/django/utils/tree.py
@@ -26,7 +26,7 @@ class Node(object):
         """
         self.children = children and children[:] or []
         self.connector = connector or self.default
-        self.subtree_parents = []
+        self.parent = None
         self.negated = negated
 
     # We need this because of django.db.models.query_utils.Q. Q. __init__() is
@@ -45,6 +45,37 @@ class Node(object):
         return obj
     _new_instance = classmethod(_new_instance)
 
+    def empty(cls):
+        return cls._new_instance([])
+    empty = classmethod(empty)
+
+
+    def clone(self, memo=None):
+        """
+        Clones the whole tree, not just the subtree. We have loops in
+        the tree due to keeping both parent and child links. Because
+        of this, we must keep a memo of objects already copied.
+        """
+        if memo is None:
+            memo = {}
+        if self in memo:
+            return memo[self]
+        obj = self.empty()
+        memo[self] = obj
+        for child in self.children:
+             if isinstance(child, Node):
+                 child = child.clone(memo=memo)
+             obj._add(child)
+        if self.parent is not None:
+            new_parent = self.parent.clone(memo=memo)
+            obj.parent = new_parent
+        obj.connector = self.connector
+        obj.negated = self.negated
+        return obj
+
+    def __repr__(self):
+        return self.as_subtree
+
     def __str__(self):
         if self.negated:
             return '(NOT (%s: %s))' % (self.connector, ', '.join([str(c) for c
@@ -52,15 +83,26 @@ class Node(object):
         return '(%s: %s)' % (self.connector, ', '.join([str(c) for c in
                 self.children]))
 
-    def __deepcopy__(self, memodict):
-        """
-        Utility method used by copy.deepcopy().
-        """
-        obj = Node(connector=self.connector, negated=self.negated)
-        obj.__class__ = self.__class__
-        obj.children = copy.deepcopy(self.children, memodict)
-        obj.subtree_parents = copy.deepcopy(self.subtree_parents, memodict)
-        return obj
+    def _as_subtree(self, indent=0):
+        buf = []
+        if self.negated:
+            buf.append(" " * indent + "NOT")
+        buf.append((" " * indent) + self.connector + ":")
+        indent += 2
+        for child in self.children:
+            if isinstance(child, Node):
+                buf.append(child._as_subtree(indent=indent))
+            else:
+                buf.append((" " * indent) + str(child))
+        return "\n".join(buf)
+    as_subtree = property(_as_subtree)
+
+    def _as_tree(self):
+        root = self
+        while root.parent:
+            root = root.parent
+        return root._as_subtree(indent=0)
+    as_tree = property(_as_tree)
 
     def __len__(self):
         """
@@ -80,74 +122,58 @@ class Node(object):
         """
         return other in self.children
 
+    def _add(self, *nodes):
+        """
+        A helper method to keep the parent/child links in valid state.
+        """
+        for node in nodes:
+            self.children.append(node)
+            if isinstance(node, Node):
+                node.parent = self
+
     def add(self, node, conn_type):
         """
-        Adds a new node to the tree. If the conn_type is the same as the root's
-        current connector type, the node is added to the first level.
+        Adds a new node to the tree. If the conn_type is the same as the
+        root's current connector type, the node is added to the first level.
         Otherwise, the whole tree is pushed down one level and a new root
-        connector is created, connecting the existing tree and the new node.
+        connector is created, connecting the existing tree and the added node.
         """
         if node in self.children and conn_type == self.connector:
             return
-        if len(self.children) < 2:
-            self.connector = conn_type
         if self.connector == conn_type:
-            if isinstance(node, Node) and (node.connector == conn_type or
-                    len(node) == 1):
-                self.children.extend(node.children)
-            else:
-                self.children.append(node)
+            self._add(node)
         else:
-            obj = self._new_instance(self.children, self.connector,
-                    self.negated)
-            self.connector = conn_type
-            self.children = [obj, node]
+            obj = self._new_instance([node], conn_type)
+            obj2 = self.clone()
+            self._add(obj, obj2)
 
     def negate(self):
         """
-        Negate the sense of the root connector. This reorganises the children
-        so that the current node has a single child: a negated node containing
-        all the previous children. This slightly odd construction makes adding
-        new children behave more intuitively.
-
-        Interpreting the meaning of this negate is up to client code. This
-        method is useful for implementing "not" arrangements.
-        """
-        self.children = [self._new_instance(self.children, self.connector,
-                not self.negated)]
-        self.connector = self.default
-
-    def start_subtree(self, conn_type):
-        """
-        Sets up internal state so that new nodes are added to a subtree of the
-        current node. The conn_type specifies how the sub-tree is joined to the
-        existing children.
+        Negate the sense of this node.
         """
-        if len(self.children) == 1:
-            self.connector = conn_type
-        elif self.connector != conn_type:
-            self.children = [self._new_instance(self.children, self.connector,
-                    self.negated)]
-            self.connector = conn_type
-            self.negated = False
+        self.negated = not self.negated
 
-        self.subtree_parents.append(self.__class__(self.children,
-                self.connector, self.negated))
-        self.connector = self.default
-        self.negated = False
-        self.children = []
+    def subtree(self, conn_type):
+        obj = self.empty()
+        obj.connector = conn_type
+        obj.parent = self
+        self.children.append(obj)
+        return obj
 
-    def end_subtree(self):
+    def prune_tree(self, recurse=False):
         """
-        Closes off the most recently unmatched start_subtree() call.
-
-        This puts the current state into a node of the parent tree and returns
-        the current instances state to be the parent.
+        Removes empty children nodes, and non-necessary intermediatry
+        nodes from this node. If recurse is true, will recurse down
+        the tree.
         """
-        obj = self.subtree_parents.pop()
-        node = self.__class__(self.children, self.connector)
-        self.connector = obj.connector
-        self.negated = obj.negated
-        self.children = obj.children
-        self.children.append(node)
-
+        old_childs = self.children[:]
+        self.children = []
+        for child in old_childs:
+            if not child:
+                continue
+            if isinstance(child, Node):
+                if recurse:
+                    child.prune_tree(recurse=True)
+                if not child.negated and len(child) == 1:
+                    child = child.children[0]
+            self._add(child)
diff --git a/tests/regressiontests/queries/tests.py b/tests/regressiontests/queries/tests.py
index d8fd5bc..4f505a3 100644
--- a/tests/regressiontests/queries/tests.py
+++ b/tests/regressiontests/queries/tests.py
@@ -820,7 +820,7 @@ class Queries1Tests(BaseQuerysetTest):
         q = Note.objects.filter(Q(extrainfo__author=self.a1)|Q(extrainfo=xx)).query
         self.assertEqual(
             len([x[2] for x in q.alias_map.values() if x[2] == q.LOUTER and q.alias_refcount[x[1]]]),
-            1
+            2
         )
 
 
