in some cases (eg ambiguous neged relations), INTERSECT should be used instead of DISTINCT
authorSylvain Thenault <sylvain.thenault@logilab.fr>
Wed, 07 Jan 2009 09:47:56 +0100
changeset 339 c0a0ce6c0428
parent 338 25aae8a15532
child 340 bfe0e95571aa
in some cases (eg ambiguous neged relations), INTERSECT should be used instead of DISTINCT
server/rqlannotation.py
server/sources/rql2sql.py
server/test/unittest_rql2sql.py
--- a/server/rqlannotation.py	Tue Jan 06 12:53:47 2009 -0800
+++ b/server/rqlannotation.py	Wed Jan 07 09:47:56 2009 +0100
@@ -20,7 +20,7 @@
     #if server.DEBUG:
     #    print '-------- sql annotate', repr(rqlst)
     getrschema = annotator.schema.rschema
-    has_text_query = False
+    has_text_query = need_intersect = False
     need_distinct = rqlst.distinct
     for rel in rqlst.iget_nodes(Relation):
         if rel.neged(strict=True):
@@ -28,14 +28,23 @@
                 need_distinct = True
             else:
                 rschema = getrschema(rel.r_type)
-                if rschema.inlined:
-                    try:
-                        var = rel.children[1].children[0].variable
-                    except AttributeError:
-                        pass # rewritten variable
+                if not rschema.is_final():
+                    # if one of the relation's variable is ambiguous, an intersection
+                    # will be necessary
+                    for vref in rel.get_nodes(VariableRef):
+                        var = vref.variable
+                        if not var.stinfo['selected'] and len(var.stinfo['possibletypes']) > 1:
+                            need_intersect = True
+                            break
                     else:
-                        if not var.stinfo['constnode']:
-                            need_distinct = True
+                        if rschema.inlined:
+                            try:
+                                var = rel.children[1].children[0].variable
+                            except AttributeError:
+                                pass # rewritten variable
+                            else:
+                                if not var.stinfo['constnode']:
+                                    need_distinct = True
         elif getrschema(rel.r_type).symetric:
             for vref in rel.iget_nodes(VariableRef):
                 stinfo = vref.variable.stinfo
@@ -139,6 +148,7 @@
             except CantSelectPrincipal:
                 stinfo['invariant'] = False
     rqlst.need_distinct = need_distinct
+    rqlst.need_intersect = need_intersect
     return has_text_query
 
 
--- a/server/sources/rql2sql.py	Tue Jan 06 12:53:47 2009 -0800
+++ b/server/sources/rql2sql.py	Wed Jan 07 09:47:56 2009 +0100
@@ -76,7 +76,7 @@
         unstable.remove(varname)
         torewrite.add(var)
         newselect = Select()
-        newselect.need_distinct = False
+        newselect.need_distinct = newselect.need_intersect = False
         myunion = Union()
         myunion.append(newselect)
         # extract aliases / selection
@@ -487,7 +487,9 @@
             elif self._state.restrictions and self.dbms_helper.needs_from_clause:
                 sql.insert(1, 'FROM (SELECT 1) AS _T')
             sqls.append('\n'.join(sql))
-        if distinct:
+        if select.need_intersect:
+            return '\nINTERSECT\n'.join(sqls)
+        elif distinct:
             return '\nUNION\n'.join(sqls)
         else:
             return '\nUNION ALL\n'.join(sqls)
--- a/server/test/unittest_rql2sql.py	Tue Jan 06 12:53:47 2009 -0800
+++ b/server/test/unittest_rql2sql.py	Wed Jan 07 09:47:56 2009 +0100
@@ -745,6 +745,14 @@
 FROM EProperty AS P
 WHERE P.for_user IS NULL'''),
 
+    ('Any S WHERE NOT X in_state S, X is IN(Affaire, EUser)',
+     '''SELECT S.eid
+FROM Affaire AS X, State AS S
+WHERE (X.in_state IS NULL OR X.in_state!=S.eid)
+INTERSECT
+SELECT S.eid
+FROM EUser AS X, State AS S
+WHERE (X.in_state IS NULL OR X.in_state!=S.eid)'''),
     ]
 
 OUTER_JOIN = [