]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- mysql: a column of type TIMESTAMP now defaults to NULL if
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 18 Dec 2009 20:09:14 +0000 (20:09 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 18 Dec 2009 20:09:14 +0000 (20:09 +0000)
"nullable=False" is not passed to Column(), and no default
is present. This is now consistent with all other types,
and in the case of TIMESTAMP explictly renders "NULL"
due to MySQL's "switching" of default nullability
for TIMESTAMP columns. [ticket:1539]

CHANGES
lib/sqlalchemy/dialects/mysql/base.py
test/dialect/test_mysql.py

diff --git a/CHANGES b/CHANGES
index 154c22a29a426fd3e6ca32ea53aaea27ba1cb001..fcd7a1163e41bc096da7cb30a6378fe9701fa10b 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -580,6 +580,13 @@ CHANGES
     - ENUM now subclasses the new generic Enum type, and also handles
       unicode values implicitly, if the given labelnames are unicode
       objects.
+    
+    - a column of type TIMESTAMP now defaults to NULL if
+      "nullable=False" is not passed to Column(), and no default
+      is present. This is now consistent with all other types, 
+      and in the case of TIMESTAMP explictly renders "NULL" 
+      due to MySQL's "switching" of default nullability
+      for TIMESTAMP columns. [ticket:1539]
       
 - oracle
     - unit tests pass 100% with cx_oracle !
index 01f8b13a7aba98edd5f68bf23c1a1e19896e8b85..e0610ec7ebf0852d56bce819d4c800512325a5e0 100644 (file)
@@ -1318,10 +1318,14 @@ class MySQLDDLCompiler(compiler.DDLCompiler):
         default = self.get_column_default_string(column)
         if default is not None:
             colspec.append('DEFAULT ' + default)
-
-        if not column.nullable:
+        
+        is_timestamp = isinstance(column.type, sqltypes.TIMESTAMP)
+        if not column.nullable and not is_timestamp:
             colspec.append('NOT NULL')
 
+        elif column.nullable and is_timestamp and default is None:
+            colspec.append('NULL')
+
         if column.primary_key and column.autoincrement:
             try:
                 first = [c for c in column.table.primary_key.columns
index 000bb131bfc8de6a679d56278d521292100fc2dc..4a419772533ba7dcd6da4c0bfeb8a2fd4a8a32b3 100644 (file)
@@ -12,7 +12,7 @@ from sqlalchemy.dialects.mysql import base as mysql
 from sqlalchemy.test.testing import eq_
 from sqlalchemy.test import *
 from sqlalchemy.test.engines import utf8_engine
-
+import datetime
 
 class TypesTest(TestBase, AssertsExecutionResults, AssertsCompiledSQL):
     "Test MySQL column types"
@@ -417,9 +417,9 @@ class TypesTest(TestBase, AssertsExecutionResults, AssertsCompiledSQL):
         try:
             columns = [
                 ([TIMESTAMP],
-                 'TIMESTAMP'),
+                 'TIMESTAMP NULL'),
                 ([mysql.MSTimeStamp],
-                 'TIMESTAMP'),
+                 'TIMESTAMP NULL'),
                 ([mysql.MSTimeStamp,
                   DefaultClause(sql.text('CURRENT_TIMESTAMP'))],
                  "TIMESTAMP DEFAULT CURRENT_TIMESTAMP"),
@@ -451,6 +451,35 @@ class TypesTest(TestBase, AssertsExecutionResults, AssertsCompiledSQL):
         finally:
             meta.drop_all()
 
+    def test_timestamp_nullable(self):
+        meta = MetaData(testing.db)
+        ts_table = Table('mysql_timestamp', meta,
+                            Column('t1', TIMESTAMP),
+                            Column('t2', TIMESTAMP, nullable=False),
+                    )
+        meta.create_all()
+        try:
+            # there's a slight assumption here that this test can
+            # complete within the scope of a single second.
+            # if needed, can break out the eq_() just to check for
+            # timestamps that are within a few seconds of "now" 
+            # using timedelta.
+            
+            now = testing.db.execute("select now()").scalar()
+            
+            # TIMESTAMP without NULL inserts current time when passed
+            # NULL.  when not passed, generates 0000-00-00 quite
+            # annoyingly.
+            ts_table.insert().execute({'t1':now, 't2':None})
+            ts_table.insert().execute({'t1':None, 't2':None})
+            
+            eq_(
+                ts_table.select().execute().fetchall(),
+                [(now, now), (None, now)]
+            )
+        finally:
+            meta.drop_all()
+            
     def test_year(self):
         """Exercise YEAR."""
 
@@ -711,7 +740,7 @@ class ReflectionTest(TestBase, AssertsExecutionResults):
             Column('c2', String(10), DefaultClause('0')),
             Column('c3', String(10), DefaultClause('abc')),
             Column('c4', TIMESTAMP, DefaultClause('2009-04-05 12:00:00')),
-            Column('c5', TIMESTAMP),
+            Column('c5', TIMESTAMP),
             
         )