[rql2sql] remove special behaviour of symmetric relation vs DISTINCT
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Fri, 20 Dec 2013 08:39:03 +0100
changeset 9518 54ead5f372bb
parent 9517 3338b2205ea3
child 9529 39b46b0b01e4
[rql2sql] remove special behaviour of symmetric relation vs DISTINCT 0542a85fe667 replacing SQL OR by hooks for symmetric relations allows that. This involve a single test value change for a border case: when querying a symmetric relation without specifying the subject nor the object, you may get some duplicated result. IMO this is fine to let the user explicitly use DISTINCT or not and to remove the dedicated handling we had which didn't let any choice. Related to #3259713
server/rqlannotation.py
server/sources/rql2sql.py
server/test/unittest_querier.py
--- a/server/rqlannotation.py	Tue Feb 11 11:03:01 2014 +0100
+++ b/server/rqlannotation.py	Fri Dec 20 08:39:03 2013 +0100
@@ -35,15 +35,6 @@
     #if server.DEBUG:
     #    print '-------- sql annotate', repr(rqlst)
     getrschema = annotator.schema.rschema
-    need_distinct = rqlst.distinct
-    for rel in rqlst.iget_nodes(Relation):
-        if getrschema(rel.r_type).symmetric and not isinstance(rel.parent, Exists):
-            for vref in rel.iget_nodes(VariableRef):
-                stinfo = vref.variable.stinfo
-                if not stinfo['constnode'] and stinfo['selected']:
-                    need_distinct = True
-                    # XXX could mark as not invariant
-                    break
     for var in rqlst.defined_vars.itervalues():
         stinfo = var.stinfo
         if stinfo.get('ftirels'):
@@ -158,7 +149,6 @@
     for col_alias in rqlst.aliases.itervalues():
         if col_alias.stinfo.get('ftirels'):
             has_text_query = True
-    rqlst.need_distinct = need_distinct
     return has_text_query
 
 
--- a/server/sources/rql2sql.py	Tue Feb 11 11:03:01 2014 +0100
+++ b/server/sources/rql2sql.py	Fri Dec 20 08:39:03 2013 +0100
@@ -116,7 +116,6 @@
             continue
         unstable.remove(varname)
         newselect = Select()
-        newselect.need_distinct = False
         myunion = Union()
         myunion.append(newselect)
         # extract aliases / selection
@@ -703,9 +702,6 @@
                             }
         if not self.dbhelper.union_parentheses_support:
             self.union_sql = self.noparen_union_sql
-        if self.dbhelper.fti_need_distinct:
-            self.__union_sql = self.union_sql
-            self.union_sql = self.has_text_need_distinct_union_sql
         self._lock = threading.Lock()
         if attrmap is None:
             attrmap = {}
@@ -739,12 +735,6 @@
         finally:
             self._lock.release()
 
-    def has_text_need_distinct_union_sql(self, union, needalias=False):
-        if getattr(union, 'has_text_query', False):
-            for select in union.children:
-                select.need_distinct = True
-        return self.__union_sql(union, needalias)
-
     def union_sql(self, union, needalias=False): # pylint: disable=E0202
         if len(union.children) == 1:
             return self.select_sql(union.children[0], needalias)
@@ -772,7 +762,12 @@
         :needwrap: boolean telling if the query will be wrapped in an outer
           query (to deal with aggregat and/or grouping)
         """
-        distinct = selectsortterms = select.need_distinct
+        if select.distinct:
+            distinct = True
+        elif self.dbhelper.fti_need_distinct:
+            distinct = getattr(select.parent, 'has_text_query', False)
+        else:
+            distinct = False
         sorts = select.orderby
         groups = select.groupby
         having = select.having
@@ -796,6 +791,7 @@
         # selection (union or distinct query) and wrapping (union with groups)
         needwrap = False
         sols = select.solutions
+        selectsortterms = distinct
         if len(sols) > 1:
             # remove invariant from solutions
             sols, existssols, unstable = remove_unused_solutions(
--- a/server/test/unittest_querier.py	Tue Feb 11 11:03:01 2014 +0100
+++ b/server/test/unittest_querier.py	Fri Dec 20 08:39:03 2013 +0100
@@ -718,17 +718,17 @@
         self.execute("INSERT Personne X: X nom 'trucmuche'")
         self.execute("SET X connait Y WHERE X nom 'chouette', Y nom 'bidule'")
         self.execute("SET X connait Y WHERE X nom 'machin', Y nom 'chouette'")
-        rset = self.execute('Any P where P connait P2')
-        self.assertEqual(len(rset.rows), 3, rset.rows)
-        rset = self.execute('Any P where NOT P connait P2')
+        rset = self.execute('Any P WHERE P connait P2')
+        self.assertEqual(len(rset.rows), 4, rset.rows)
+        rset = self.execute('Any P WHERE NOT P connait P2')
         self.assertEqual(len(rset.rows), 1, rset.rows) # trucmuche
-        rset = self.execute('Any P where P connait P2, P2 nom "bidule"')
+        rset = self.execute('Any P WHERE P connait P2, P2 nom "bidule"')
         self.assertEqual(len(rset.rows), 1, rset.rows)
-        rset = self.execute('Any P where P2 connait P, P2 nom "bidule"')
+        rset = self.execute('Any P WHERE P2 connait P, P2 nom "bidule"')
         self.assertEqual(len(rset.rows), 1, rset.rows)
-        rset = self.execute('Any P where P connait P2, P2 nom "chouette"')
+        rset = self.execute('Any P WHERE P connait P2, P2 nom "chouette"')
         self.assertEqual(len(rset.rows), 2, rset.rows)
-        rset = self.execute('Any P where P2 connait P, P2 nom "chouette"')
+        rset = self.execute('Any P WHERE P2 connait P, P2 nom "chouette"')
         self.assertEqual(len(rset.rows), 2, rset.rows)
 
     def test_select_inline(self):