[Erp5-report] r33660 vincent - in /erp5/trunk/products/ZSQLCatalog: ./ tests/

nobody at svn.erp5.org nobody at svn.erp5.org
Thu Mar 11 17:05:45 CET 2010


Author: vincent
Date: Thu Mar 11 17:05:44 2010
New Revision: 33660

URL: http://svn.erp5.org?rev=33660&view=rev
Log:
Fix regression introduced in r33653.

As the generated order-by expressions now (rightly) differ based on data, the
exception generated when detecting the difference must be postponed to when
expression is actualy used, as it might not be used.
Also, test that postponing effectively works, and demonstrate another way to
express the same condition, but which allows sorting.

Modified:
    erp5/trunk/products/ZSQLCatalog/SQLExpression.py
    erp5/trunk/products/ZSQLCatalog/tests/testSQLCatalog.py

Modified: erp5/trunk/products/ZSQLCatalog/SQLExpression.py
URL: http://svn.erp5.org/erp5/trunk/products/ZSQLCatalog/SQLExpression.py?rev=33660&r1=33659&r2=33660&view=diff
==============================================================================
--- erp5/trunk/products/ZSQLCatalog/SQLExpression.py [utf8] (original)
+++ erp5/trunk/products/ZSQLCatalog/SQLExpression.py [utf8] Thu Mar 11 17:05:44 2010
@@ -54,6 +54,32 @@
     return {}
   assert isinstance(value, dict)
   return value.copy()
+
+class MergeConflictError(ValueError):
+  pass
+
+class MergeConflict(object):
+  """
+  This class allows lazy errors.
+
+  SQLExpression detects merge conflicts when 2 different values exist for the
+  same key in 2 SQLExpression tree nodes.
+  This class allows to postpone raising an exception, to allow conflicting
+  values as long as they are not actualy used.
+  """
+  # TODO (?): Include the traceback as of instanciation in error message,
+  #           if it can help debugging.
+  def __init__(self, message):
+    self._message = message
+
+  def __call__(self):
+    raise MergeConflictError(self._message)
+
+def conflictSafeGet(dikt, key, default=None):
+  result = dikt.get(key, default)
+  if isinstance(result, MergeConflict):
+    result() # Raises
+  return result
 
 class SQLExpression(object):
 
@@ -180,12 +206,13 @@
     return result
 
   @profiler_decorator
-  def getOrderByDict(self):
+  def _getOrderByDict(self, delay_error=True):
     result_dict = self.order_by_dict.copy()
     for sql_expression in self.sql_expression_list:
-      order_by_dict = sql_expression.getOrderByDict()
+      order_by_dict = sql_expression._getOrderByDict(delay_error=delay_error)
       for key, value in order_by_dict.iteritems():
-        if key in result_dict and value != result_dict[key]:
+        if key in result_dict and value != result_dict[key] \
+            and not isinstance(value, MergeConflict):
           message = 'I don\'t know how to merge order_by_dict with ' \
                     'conflicting entries for key %r: %r vs. %r' % (key, result_dict[key], value)
           if DEBUG:
@@ -194,10 +221,16 @@
               sql_expression,
               sql_expression.query,
               ', '.join('%r (%r)' % (x, x.query) for x in self.sql_expression_list))
-          raise ValueError, message
+          if delay_error:
+            order_by_dict[key] = MergeConflict(message)
+          else:
+            raise MergeConflictError, message
       result_dict.update(order_by_dict)
     return result_dict
 
+  def getOrderByDict(self):
+    return self._getOrderByDict(delay_error=False)
+
   @profiler_decorator
   def getOrderByExpression(self):
     """
@@ -205,9 +238,8 @@
 
       Returns a rendered "order by" expression. See getOrderByList.
     """
-    order_by_dict = self.getOrderByDict()
-    get = order_by_dict.get
-    return SQL_LIST_SEPARATOR.join(get(x, str(x)) \
+    order_by_dict = self._getOrderByDict()
+    return SQL_LIST_SEPARATOR.join(conflictSafeGet(order_by_dict, x, str(x)) \
                                    for x in self.getOrderByList())
 
   @profiler_decorator

Modified: erp5/trunk/products/ZSQLCatalog/tests/testSQLCatalog.py
URL: http://svn.erp5.org/erp5/trunk/products/ZSQLCatalog/tests/testSQLCatalog.py?rev=33660&r1=33659&r2=33660&view=diff
==============================================================================
--- erp5/trunk/products/ZSQLCatalog/tests/testSQLCatalog.py [utf8] (original)
+++ erp5/trunk/products/ZSQLCatalog/tests/testSQLCatalog.py [utf8] Thu Mar 11 17:05:44 2010
@@ -35,6 +35,7 @@
 from Products.ZSQLCatalog.Query.EntireQuery import EntireQuery
 from Products.ZSQLCatalog.Query.RelatedQuery import RelatedQuery
 from DateTime import DateTime
+from Products.ZSQLCatalog.SQLExpression import MergeConflictError
 
 class ReferenceQuery:
   """
@@ -417,9 +418,21 @@
                  {'keyword': '<"=a OR =b"'})
     self.catalog(ReferenceQuery(ReferenceQuery(operator='like', keyword='%"a" OR "b"%'), operator='and'),
                  {'keyword': '"\\"a\\" OR \\"b\\""'})
-    self.catalog(ReferenceQuery(ReferenceQuery(ReferenceQuery(operator='match', fulltext='a'),
-                                               ReferenceQuery(ReferenceQuery(operator='match', fulltext='b'), operator='not'), operator='and'), operator='and'),
-                 {'fulltext': 'a NOT b'})
+    # This example introduces impossible-to-merge search text criterion, which
+    # is allowed as long as 
+    reference_query = ReferenceQuery(
+        ReferenceQuery(ReferenceQuery(operator='match', fulltext='a'),
+        ReferenceQuery(ReferenceQuery(operator='match', fulltext='b'),
+      operator='not'), operator='and'), operator='and')
+    self.catalog(reference_query, {'fulltext': 'a NOT b'})
+    # The same, with an order by, must raise
+    self.assertRaises(MergeConflictError, self.catalog, reference_query,
+      {'fulltext': 'a NOT b', 'order_by_list': [('fulltext', ), ]},
+      check_search_text=False)
+    # If one want to sort on, he must use the equivalent FullText syntax:
+    self.catalog(ReferenceQuery(ReferenceQuery(operator='match_boolean', fulltext='a -b'), operator='and'),
+      {'fulltext': 'a -b', 'order_by_list': [('fulltext', ), ]},
+      check_search_text=False)
     self.catalog(ReferenceQuery(ReferenceQuery(ReferenceQuery(operator='match', fulltext='a'),
                                                ReferenceQuery(ReferenceQuery(operator='match', fulltext='b'), operator='not'), operator='or'), operator='and'),
                  {'fulltext': 'a OR NOT b'})




More information about the Erp5-report mailing list