[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