[CalendarServer-changes] [9531] CalendarServer/trunk/twext/enterprise/dal

source_changes at macosforge.org source_changes at macosforge.org
Mon Aug 6 14:39:47 PDT 2012


Revision: 9531
          http://trac.macosforge.org/projects/calendarserver/changeset/9531
Author:   cdaboo at apple.com
Date:     2012-08-06 14:39:46 -0700 (Mon, 06 Aug 2012)
Log Message:
-----------
More DAL tweaks.

Modified Paths:
--------------
    CalendarServer/trunk/twext/enterprise/dal/syntax.py
    CalendarServer/trunk/twext/enterprise/dal/test/test_sqlsyntax.py

Modified: CalendarServer/trunk/twext/enterprise/dal/syntax.py
===================================================================
--- CalendarServer/trunk/twext/enterprise/dal/syntax.py	2012-08-06 17:40:14 UTC (rev 9530)
+++ CalendarServer/trunk/twext/enterprise/dal/syntax.py	2012-08-06 21:39:46 UTC (rev 9531)
@@ -325,13 +325,20 @@
 
 
     def In(self, other):
-        # Can't be Select.__contains__ because __contains__ gets __nonzero__
-        # called on its result by the 'in' syntax.
+        """
+        We support two forms of the SQL "IN" syntax: one where a list of values is supplied, the other where
+        a sub-select is used to provide a set of values.
+
+        @param other: a constant parameter or sub-select
+        @type other: L{Parameter} or L{Select}
+        """
         if isinstance(other, Parameter):
-            if not other.isSet():
-                raise DALError("Parameter in an In(...) expression must be a set of values.")
+            if other.count is None:
+                raise DALError("IN expression needs an explicit count of parameters")
             return CompoundComparison(self, 'in', Constant(other))
         else:
+            # Can't be Select.__contains__ because __contains__ gets __nonzero__
+            # called on its result by the 'in' syntax.
             return CompoundComparison(self, 'in', other)
 
 
@@ -373,6 +380,10 @@
 
 
 class Constant(ExpressionSyntax):
+    """
+    Generates an expression for a place holder where a value will be bound to the query. If the constant is a Parameter
+    with count > 1 then a parenthesized, comma-separated list of place holders will be generated.
+    """
     def __init__(self, value):
         self.value = value
 
@@ -382,9 +393,9 @@
 
 
     def subSQL(self, queryGenerator, allTables):
-        if isinstance(self.value, Parameter) and self.value.isSet():
+        if isinstance(self.value, Parameter) and self.value.count is not None:
             return _inParens(_CommaList(
-                [SQLFragment(queryGenerator.placeholder.placeholder(), [self.value] if ctr == 0 else []) for ctr in range(self.value.len)]
+                [SQLFragment(queryGenerator.placeholder.placeholder(), [self.value] if ctr == 0 else []) for ctr in range(self.value.count)]
             ).subSQL(queryGenerator, allTables))
         else:
             return SQLFragment(queryGenerator.placeholder.placeholder(), [self.value])
@@ -1602,7 +1613,9 @@
         params = []
         for parameter in self.parameters:
             if isinstance(parameter, Parameter):
-                if parameter.isSet():
+                if parameter.count is not None:
+                    if parameter.count != len(kw[parameter.name]):
+                        raise DALError("Number of place holders does not match number of items to bind")
                     for item in kw[parameter.name]:
                         params.append(item)
                 else:
@@ -1640,18 +1653,23 @@
 
 
 class Parameter(object):
+    """
+    Used to represent a place holder for a value to be bound to the query
+    at a later date. If count > 1, then a "set" of parenthesized,
+    comma separate place holders will be generated.
+    """
 
-    def __init__(self, name, values=None):
+    def __init__(self, name, count=None):
         self.name = name
-        self.values = values
-        if self.values is not None:
-            self.len = len(values)
+        self.count = count
+        if self.count is not None and self.count < 1:
+            raise DALError("Must have Parameter.count > 0")
 
 
     def __eq__(self, param):
         if not isinstance(param, Parameter):
             return NotImplemented
-        return self.name == param.name
+        return self.name == param.name and self.count == param.count
 
 
     def __ne__(self, param):
@@ -1664,11 +1682,7 @@
         return 'Parameter(%r)' % (self.name,)
 
 
-    def isSet(self):
-        return hasattr(self, "len")
 
-
-
 # Common helpers:
 
 # current timestamp in UTC format.  Hack to support standard syntax for this,

Modified: CalendarServer/trunk/twext/enterprise/dal/test/test_sqlsyntax.py
===================================================================
--- CalendarServer/trunk/twext/enterprise/dal/test/test_sqlsyntax.py	2012-08-06 17:40:14 UTC (rev 9530)
+++ CalendarServer/trunk/twext/enterprise/dal/test/test_sqlsyntax.py	2012-08-06 21:39:46 UTC (rev 9531)
@@ -694,33 +694,43 @@
                 "select * from FOO where BAR in (select QUX from BOZ)"))
 
 
-    def test_inParameterSet(self):
+    def test_inParameter(self):
         """
         L{ColumnSyntax.In} returns a sub-expression using the SQL 'in' syntax with
         parameter list.
         """
         
+        # One item with IN only
+        items = set(('A',))
+        self.assertEquals(
+            Select(From=self.schema.FOO, Where=self.schema.FOO.BAR.In(Parameter("names", len(items)))).toSQL().bind(names=items),
+            SQLFragment(
+                "select * from FOO where BAR in (?)", ['A']))
+
+        # Two items with IN only
         items = set(('A', 'B'))
         self.assertEquals(
-            Select(From=self.schema.FOO, Where=self.schema.FOO.BAR.In(Parameter("names", items))).toSQL().bind(names=items),
+            Select(From=self.schema.FOO, Where=self.schema.FOO.BAR.In(Parameter("names", len(items)))).toSQL().bind(names=items),
             SQLFragment(
                 "select * from FOO where BAR in (?, ?)", ['A', 'B']))
 
+        # Two items with preceding AND
         self.assertEquals(
             Select(
                 From=self.schema.FOO,
                 Where=(self.schema.FOO.BAZ == Parameter('P1')).And(
-                    self.schema.FOO.BAR.In(Parameter("names", items)
+                    self.schema.FOO.BAR.In(Parameter("names", len(items))
                 ))
             ).toSQL().bind(P1="P1", names=items),
             SQLFragment(
                 "select * from FOO where BAZ = ? and BAR in (?, ?)", ['P1', 'A', 'B']),
         )
 
+        # Two items with following AND
         self.assertEquals(
             Select(
                 From=self.schema.FOO,
-                Where=(self.schema.FOO.BAR.In(Parameter("names", items)).And(
+                Where=(self.schema.FOO.BAR.In(Parameter("names", len(items))).And(
                     self.schema.FOO.BAZ == Parameter('P2')
                 ))
             ).toSQL().bind(P2="P2", names=items),
@@ -728,11 +738,12 @@
                 "select * from FOO where BAR in (?, ?) and BAZ = ?", ['A', 'B', 'P2']),
         )
 
+        # Two items with preceding OR and following AND
         self.assertEquals(
             Select(
                 From=self.schema.FOO,
                 Where=(self.schema.FOO.BAZ == Parameter('P1')).Or(
-                    self.schema.FOO.BAR.In(Parameter("names", items)).And(
+                    self.schema.FOO.BAR.In(Parameter("names", len(items))).And(
                         self.schema.FOO.BAZ == Parameter('P2')
                 ))
             ).toSQL().bind(P1="P1", P2="P2", names=items),
@@ -740,8 +751,20 @@
                 "select * from FOO where BAZ = ? or BAR in (?, ?) and BAZ = ?", ['P1', 'A', 'B', 'P2']),
         )
 
-        # Check that a set argument is required
+        # Check various error situations
+        
+        # No count not allowed
         self.assertRaises(DALError, self.schema.FOO.BAR.In, Parameter("names"))
+        
+        # count=0 not allowed
+        self.assertRaises(DALError, Parameter,"names", 0)
+        
+        # Mismatched count and len(items)
+        self.assertRaises(
+            DALError,
+            Select(From=self.schema.FOO, Where=self.schema.FOO.BAR.In(Parameter("names", len(items)))).toSQL().bind,
+            names=["a", "b", "c",]
+        )
 
 
     def test_max(self):
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120806/26c53ca4/attachment.html>


More information about the calendarserver-changes mailing list