]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Render parenthesis around sqlite expression defaults
authorBartlomiej Biernacki <pax0r@o2.pl>
Thu, 31 Jan 2019 14:33:01 +0000 (09:33 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 2 Feb 2019 03:04:22 +0000 (22:04 -0500)
Fixed bug in SQLite DDL where using an expression as a server side default
required that it be contained within parenthesis to be accepted by the
sqlite parser.  Pull request courtesy Bartlomiej Biernacki.

Fixes: #4474
Closes: #4475
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/4475
Pull-request-sha: 0af3238c69c2610333cf62082c12047d45c31ce0

Change-Id: I888c69bfba5a5ab8ffa420f512557ac311a36b31
(cherry picked from commit f7ce37e9dd493d4b60b3156f08ef4c98fd63904d)

doc/build/changelog/unreleased_12/4474.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/sqlite/base.py
test/dialect/test_sqlite.py

diff --git a/doc/build/changelog/unreleased_12/4474.rst b/doc/build/changelog/unreleased_12/4474.rst
new file mode 100644 (file)
index 0000000..ba4c6b7
--- /dev/null
@@ -0,0 +1,7 @@
+.. change::
+   :tags: bug, sqlite
+   :tickets: 4474
+
+   Fixed bug in SQLite DDL where using an expression as a server side default
+   required that it be contained within parenthesis to be accepted by the
+   sqlite parser.  Pull request courtesy Bartlomiej Biernacki.
index bc7f7fce41e23e8ccf499bf3049cae32cce75ea8..d6a7139880b875c31d75cfbc77cc381bf327def6 100644 (file)
@@ -583,6 +583,7 @@ from ... import types as sqltypes
 from ... import util
 from ...engine import default
 from ...engine import reflection
+from ...sql import ColumnElement
 from ...sql import compiler
 from ...types import BLOB  # noqa
 from ...types import BOOLEAN  # noqa
@@ -1034,6 +1035,8 @@ class SQLiteDDLCompiler(compiler.DDLCompiler):
         colspec = self.preparer.format_column(column) + " " + coltype
         default = self.get_column_default_string(column)
         if default is not None:
+            if isinstance(column.server_default.arg, ColumnElement):
+                default = "(" + default + ")"
             colspec += " DEFAULT " + default
 
         if not column.nullable:
index adfca5b5346d578169c1984f68f37db578b35e6c..b9a5c28c1b0fa6f5947aa9e39addf171659f5e97 100644 (file)
@@ -504,12 +504,49 @@ class DefaultsTest(fixtures.TestBase, AssertsCompiledSQL):
             Column("x", Boolean, server_default=sql.false()),
         )
         t.create(testing.db)
-        testing.db.execute(t.insert())
-        testing.db.execute(t.insert().values(x=True))
-        eq_(
-            testing.db.execute(t.select().order_by(t.c.x)).fetchall(),
-            [(False,), (True,)],
+        with testing.db.connect() as conn:
+            conn.execute(t.insert())
+            conn.execute(t.insert().values(x=True))
+            eq_(
+                conn.execute(t.select().order_by(t.c.x)).fetchall(),
+                [(False,), (True,)],
+            )
+
+    @testing.provide_metadata
+    def test_function_default(self):
+        t = Table(
+            "t",
+            self.metadata,
+            Column("id", Integer, primary_key=True),
+            Column("x", DateTime(), server_default=func.now()),
+        )
+        t.create(testing.db)
+        with testing.db.connect() as conn:
+            now = conn.scalar(func.now())
+            today = datetime.datetime.today()
+            conn.execute(t.insert())
+            conn.execute(t.insert().values(x=today))
+            eq_(
+                conn.execute(select([t.c.x]).order_by(t.c.id)).fetchall(),
+                [(now,), (today,)],
+            )
+
+    @testing.provide_metadata
+    def test_expression_with_function_default(self):
+        t = Table(
+            "t",
+            self.metadata,
+            Column("id", Integer, primary_key=True),
+            Column("x", Integer(), server_default=func.abs(-5) + 17),
         )
+        t.create(testing.db)
+        with testing.db.connect() as conn:
+            conn.execute(t.insert())
+            conn.execute(t.insert().values(x=35))
+            eq_(
+                conn.execute(select([t.c.x]).order_by(t.c.id)).fetchall(),
+                [(22,), (35,)],
+            )
 
     def test_old_style_default(self):
         """test non-quoted integer value on older sqlite pragma"""
@@ -851,6 +888,36 @@ class SQLTest(fixtures.TestBase, AssertsCompiledSQL):
             ")",
         )
 
+    def test_column_defaults_ddl(self):
+
+        t = Table(
+            "t", MetaData(), Column("x", Boolean, server_default=sql.false())
+        )
+
+        self.assert_compile(
+            CreateTable(t),
+            "CREATE TABLE t (x BOOLEAN DEFAULT (0), CHECK (x IN (0, 1)))",
+        )
+
+        t = Table(
+            "t",
+            MetaData(),
+            Column("x", String(), server_default=func.sqlite_version()),
+        )
+        self.assert_compile(
+            CreateTable(t),
+            "CREATE TABLE t (x VARCHAR DEFAULT (sqlite_version()))",
+        )
+
+        t = Table(
+            "t",
+            MetaData(),
+            Column("x", Integer(), server_default=func.abs(-5) + 17),
+        )
+        self.assert_compile(
+            CreateTable(t), "CREATE TABLE t (x INTEGER DEFAULT (abs(-5) + 17))"
+        )
+
     def test_create_partial_index(self):
         m = MetaData()
         tbl = Table("testtbl", m, Column("data", Integer))