[CalendarServer-changes] [8774] CalendarServer/branches/users/glyph/case-insensitive-uid

source_changes at macosforge.org source_changes at macosforge.org
Wed Feb 29 10:14:54 PST 2012


Revision: 8774
          http://trac.macosforge.org/projects/calendarserver/changeset/8774
Author:   glyph at apple.com
Date:     2012-02-29 10:14:54 -0800 (Wed, 29 Feb 2012)
Log Message:
-----------
Support for basic check constraints.

Modified Paths:
--------------
    CalendarServer/branches/users/glyph/case-insensitive-uid/twext/enterprise/dal/parseschema.py
    CalendarServer/branches/users/glyph/case-insensitive-uid/twext/enterprise/dal/test/test_parseschema.py

Property Changed:
----------------
    CalendarServer/branches/users/glyph/case-insensitive-uid/

Modified: CalendarServer/branches/users/glyph/case-insensitive-uid/twext/enterprise/dal/parseschema.py
===================================================================
--- CalendarServer/branches/users/glyph/case-insensitive-uid/twext/enterprise/dal/parseschema.py	2012-02-29 18:14:50 UTC (rev 8773)
+++ CalendarServer/branches/users/glyph/case-insensitive-uid/twext/enterprise/dal/parseschema.py	2012-02-29 18:14:54 UTC (rev 8774)
@@ -24,13 +24,17 @@
 from sqlparse import parse, keywords
 from sqlparse.tokens import Keyword, Punctuation, Number, String, Name
 from sqlparse.sql import (Comment, Identifier, Parenthesis, IdentifierList,
-                          Function)
+                          Function, Comparison)
 
 from twext.enterprise.dal.model import (
     Schema, Table, SQLType, ProcedureCall, Constraint, Sequence, Index)
 
+from twext.enterprise.dal.syntax import (
+    ColumnSyntax, CompoundComparison, Constant
+)
 
 
+
 def _fixKeywords():
     """
     Work around bugs in SQLParse, adding SEQUENCE as a keyword (since it is
@@ -250,12 +254,47 @@
         return idnames
 
 
+    def readExpression(self, parens):
+        """
+        Read a given expression from a Parenthesis object.  (This is currently
+        a limited parser in support of simple CHECK constraints, not something
+        suitable for a full WHERE Clause.)
+        """
+        parens = iterSignificant(parens)
+        expect(parens, ttype=Punctuation, value="(")
+        nexttok = parens.next()
+        if isinstance(nexttok, Comparison):
+            lhs, op, rhs = list(iterSignificant(nexttok))
+            result = CompoundComparison(self.nameOrValue(lhs),
+                                        op.value.encode("ascii"),
+                                        self.nameOrValue(rhs))
+        elif isinstance(nexttok, Identifier):
+            result = None
+        expect(parens, ttype=Punctuation, value=")")
+        return result
+
+
+    def nameOrValue(self, tok):
+        """
+        Inspecting a token present in an expression (for a CHECK constraint on
+        this table), return a L{twext.enterprise.dal.syntax} object for that
+        value.
+        """
+        if isinstance(tok, Identifier):
+            return ColumnSyntax(self.table.columnNamed(tok.get_name()))
+        elif tok.ttype == Number.Integer:
+            return Constant(int(tok.value))
+
+
     def parseConstraint(self, constraintType):
         """
         Parse a 'free' constraint, described explicitly in the table as opposed
         to being implicitly associated with a column by being placed after it.
         """
         # only know about PRIMARY KEY and UNIQUE for now
+        if constraintType.match(Keyword, 'CONSTRAINT'):
+            expect(self, cls=Identifier) # constraintName
+            constraintType = expect(self, ttype=Keyword)
         if constraintType.match(Keyword, 'PRIMARY'):
             expect(self, ttype=Keyword, value='KEY')
             names = self.namesInParens(expect(self, cls=Parenthesis))
@@ -263,6 +302,8 @@
         elif constraintType.match(Keyword, 'UNIQUE'):
             names = self.namesInParens(expect(self, cls=Parenthesis))
             self.table.tableConstraint(Constraint.UNIQUE, names)
+        elif constraintType.match(Keyword, 'CHECK'):
+            self.table.checkConstraint(self.readExpression(self.next()))
         else:
             raise ViolatedExpectation('PRIMARY or UNIQUE', constraintType)
         return self.checkEnd(self.next())
@@ -313,8 +354,7 @@
             else:
                 expected = True
                 def oneConstraint(t):
-                    self.table.tableConstraint(t,
-                                               [theColumn.name])
+                    self.table.tableConstraint(t, [theColumn.name])
 
                 if val.match(Keyword, 'PRIMARY'):
                     expect(self, ttype=Keyword, value='KEY')
@@ -330,6 +370,8 @@
                     oneConstraint(Constraint.NOT_NULL)
                 elif val.match(Keyword, 'NOT NULL'):
                     oneConstraint(Constraint.NOT_NULL)
+                elif val.match(Keyword, 'CHECK'):
+                    self.table.checkConstraint(self.readExpression(self.next()))
                 elif val.match(Keyword, 'DEFAULT'):
                     theDefault = self.next()
                     if isinstance(theDefault, Function):
@@ -478,7 +520,7 @@
 
 def _destringify(strval):
     """
-    Convert a single-quoted SQL string into its actual represented value.
+    Convert a single-quoted SQL string into its actual repsresented value.
     (Assumes standards compliance, since we should be controlling all the input
     here.  The only quoting syntax respected is "''".)
     """

Modified: CalendarServer/branches/users/glyph/case-insensitive-uid/twext/enterprise/dal/test/test_parseschema.py
===================================================================
--- CalendarServer/branches/users/glyph/case-insensitive-uid/twext/enterprise/dal/test/test_parseschema.py	2012-02-29 18:14:50 UTC (rev 8773)
+++ CalendarServer/branches/users/glyph/case-insensitive-uid/twext/enterprise/dal/test/test_parseschema.py	2012-02-29 18:14:54 UTC (rev 8774)
@@ -195,13 +195,59 @@
         """
         for identicalSchema in [
                 "create table sample (example integer unique);",
-                "create table sample (example integer, unique(example));"]:
+                "create table sample (example integer, unique(example));",
+                "create table sample "
+                "(example integer, constraint unique_example unique(example))"]:
             s = self.schemaFromString(identicalSchema)
             table = s.tableNamed('sample')
             column = table.columnNamed('example')
             self.assertEquals(list(table.uniques()), [[column]])
 
 
+    def test_checkExpressionConstraint(self):
+        """
+        A column with a CHECK constraint in SQL that uses an inequality will
+        result in a L{Check} constraint being added to the L{Table} object.
+        """
+        def checkOneConstraint(sqlText):
+            s = self.schemaFromString(sqlText)
+            table = s.tableNamed('sample')
+            self.assertEquals(len(table.constraints), 1)
+            constraint = table.constraints[0]
+            from twext.enterprise.dal.syntax import CompoundComparison
+            expr = constraint.expression
+            self.assertIsInstance(expr, CompoundComparison)
+            self.assertEqual(expr.a.model, table.columnNamed('example'))
+            self.assertEqual(expr.b.value, 5)
+            self.assertEqual(expr.op, '>')
+        checkOneConstraint(
+            "create table sample (example integer check(example >  5));"
+        )
+        checkOneConstraint(
+            "create table sample (example integer, check(example  > 5));"
+        )
+        checkOneConstraint(
+            "create table sample "
+            "(example integer, constraint gt_5 check(example>5))"
+        )
+
+
+    def test_checkKeywordConstraint(self):
+        """
+        A column with a CHECK constraint in SQL that compares with a keyword
+        expression such as 'lower' will result in a L{Check} constraint being
+        added to the L{Table} object.
+        """
+        def checkOneConstraint(sqlText):
+            s = self.schemaFromString(sqlText)
+            table = s.tableNamed('sample')
+            self.assertEquals(len(table.constraints), 1)
+        checkOneConstraint(
+            "create table sample "
+            "(example integer check(example = lower(example)));"
+        )
+
+
     def test_multiUnique(self):
         """
         A column with a UNIQUE constraint in SQL will result in the table
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120229/6822d855/attachment-0001.html>


More information about the calendarserver-changes mailing list