From 5ec712f89149a3139c0adbdca73483fa556d49c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Fri, 30 Apr 2010 13:54:39 +0000
Subject: [PATCH] Use Queries instead of hand crafted SQL. This removes the
 need of "predicate.uid != 0" trick to force the table mapping, and therefore
 removes one warning from catalog; there is still a warning because
 'from_expression', but this one would be harder to remove.

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@34897 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5/Tool/DomainTool.py | 104 ++++++++++++++++----------------
 1 file changed, 53 insertions(+), 51 deletions(-)

diff --git a/product/ERP5/Tool/DomainTool.py b/product/ERP5/Tool/DomainTool.py
index 721fbc55f9..da7311029e 100644
--- a/product/ERP5/Tool/DomainTool.py
+++ b/product/ERP5/Tool/DomainTool.py
@@ -32,7 +32,7 @@ from Products.ERP5Type.Globals import InitializeClass, DTMLFile
 from Products.ERP5Type import Permissions
 from Products.ERP5 import _dtmldir
 from Products.ERP5Type.Tool.BaseTool import BaseTool
-from Products.ZSQLCatalog.SQLCatalog import SQLQuery
+from Products.ZSQLCatalog.SQLCatalog import SQLQuery, Query, ComplexQuery
 from zLOG import LOG
 from DateTime import DateTime
 
@@ -61,7 +61,7 @@ class DomainTool(BaseTool):
                             ignored_category_list=None,
                             tested_base_category_list=None,
                             filter_method=None, acquired=1,
-                            strict=True, sort_key_method=None, **kw):
+                            strict=True, sort_key_method=None, query=None, **kw):
       # XXX: about "strict" parameter: This is a transition parameter,
       # allowing someone hitting a bug to revert to original behaviour easily.
       # It is not a correct name, as pointed out by Jerome. But instead of
@@ -111,51 +111,62 @@ class DomainTool(BaseTool):
       expression_list = []
       checked_column_list = []
       sql_kw = {}
+      query_list = []
+      if query is not None:
+        query_list = [query]
+
       for column in column_list:
         if column not in checked_column_list:
           range_property = 0
           if (column.endswith('_range_min')) or \
              (column.endswith('_range_max')):
             range_property = 1
-            property = column[:-len('_range_min')]
+            property_name = column[:-len('_range_min')]
           if ('%s_range_min' % column) in column_list:
             range_property = 1
-            property = column
+            property_name = column
           if range_property:
             # We have to check a range property
-            base_name = 'predicate.%s' % property
-#             LOG('searchPredicateList, getPath', 0, context.getPath())
-#             LOG('searchPredicateList, base_name', 0, base_name)
-#             LOG('searchPredicateList, property', 0, property)
-#             LOG('searchPredicateList, getProperty', 0,
-#                 context.getProperty(property))
-            value = context.getProperty(property)
-            format_dict = {'base_name': base_name}
-            expression = "((%(base_name)s is NULL) AND " \
-                         "(%(base_name)s_range_min is NULL) AND " \
-                         "(%(base_name)s_range_max is NULL))" % format_dict
+            equality = 'predicate.%s' % property_name
+            range_min = 'predicate.%s_range_min' % property_name
+            range_max = 'predicate.%s_range_max' % property_name
+            
+            value = context.getProperty(property_name)
+
+            query = ComplexQuery(
+                Query(**{equality: None}),
+                Query(**{range_min: None}),
+                Query(**{range_max: None}),
+                logical_operator='AND')
+
             if value is not None:
-              # Handle Mysql datetime correctly
-              if isinstance(value, DateTime):
-                value = value.toZone('UTC').ISO()
-              format_dict['value'] = value
-              # Generate expression
-              expression += " OR (%(base_name)s = '%(value)s') " \
-                          "OR ((%(base_name)s_range_min <= '%(value)s') AND " \
-                              "(%(base_name)s_range_max is NULL)) " \
-                          "OR ((%(base_name)s_range_min is NULL) AND " \
-                              "%(base_name)s_range_max > '%(value)s') " \
-                          "OR ((%(base_name)s_range_min <= '%(value)s') AND " \
-                              "%(base_name)s_range_max > '%(value)s') " \
-                            % format_dict
-            expression = '( %s )' % expression
-            expression_list.append(expression)
-            checked_column_list.append('%s' % property)
-            checked_column_list.append('%s_range_min' % property)
-            checked_column_list.append('%s_range_max' % property)
-      # Add predicate.uid for automatic join
-      sql_kw['predicate.uid'] = '!=0'
-      where_expression = ' AND \n'.join(expression_list)
+              query = ComplexQuery(
+                  query,
+                  ComplexQuery(
+                    Query(**{equality: value}),
+                    ComplexQuery(
+                      ComplexQuery(
+                        Query(**{range_min: dict(query=value, range='ngt',)}),
+                        Query(**{range_max: None}),
+                        logical_operator='AND',),
+                      ComplexQuery(
+                        Query(**{range_min: None}),
+                        Query(**{range_max: dict(query=value, range='min',)}),
+                        logical_operator='AND',),
+                      ComplexQuery(
+                        Query(**{range_min: dict(query=value, range='ngt',)}),
+                        Query(**{range_max: dict(query=value, range='min',)}),
+                        logical_operator='AND',),
+                      logical_operator='OR',),
+                    logical_operator='OR',
+                    ),
+                  logical_operator='OR')
+
+            query_list.append(query)
+
+            checked_column_list.append('%s' % property_name)
+            checked_column_list.append('%s_range_min' % property_name)
+            checked_column_list.append('%s_range_max' % property_name)
 
       # Add category selection
       if tested_base_category_list is None:
@@ -177,44 +188,35 @@ class DomainTool(BaseTool):
                                            query_table='predicate_category',
                                            none_sql_value=0,
                                            strict=strict)
-        category_expression = category_expression_dict['where_expression']
+        where_expression = category_expression_dict['where_expression']
+        if where_expression:
+          sql_kw['where_expression'] = SQLQuery(where_expression)
+
         if 'from_expression' in category_expression_dict:
           sql_kw['from_expression'] = category_expression_dict['from_expression']
         else:
           # Add predicate_category.uid for automatic join
           sql_kw['predicate_category.uid'] = '!=NULL'
-        if len(where_expression) > 0:
-          where_expression = '(%s) AND \n(%s)' % \
-                                          (where_expression,category_expression)
-        else:
-          where_expression = category_expression
 
-      if len(where_expression):
-        sql_kw['where_expression'] = SQLQuery(where_expression)
-      else:
-        sql_kw['where_expression'] = ''
       kw.update(sql_kw)
-#       LOG('searchPredicateList, kw',0,kw)
+      if query_list:
+        kw['query'] = ComplexQuery(logical_operator='AND', *query_list)
 
       sql_result_list = portal_catalog.searchResults(**kw)
       if kw.get('src__'):
         return sql_result_list
       result_list = []
-#       LOG('searchPredicateList, result_list before test', 0,
-#           [x.getObject() for x in sql_result_list])
       for predicate in [x.getObject() for x in sql_result_list]:
         if (not test) or predicate.test(
                        context,
                        tested_base_category_list=tested_base_category_list):
           result_list.append(predicate)
-#       LOG('searchPredicateList, result_list before sort', 0, result_list)
       if filter_method is not None:
         result_list = filter_method(result_list)
       if sort_key_method is not None:
         result_list.sort(key=sort_key_method)
       elif sort_method is not None:
         result_list.sort(cmp=sort_method)
-#       LOG('searchPredicateList, result_list after sort', 0, result_list)
       return result_list
 
     # XXX FIXME method should not be public 
-- 
2.30.9