From: Bartlomiej Biernacki Date: Thu, 31 Jan 2019 14:33:01 +0000 (-0500) Subject: Render parenthesis around sqlite expression defaults X-Git-Tag: rel_1_3_0b3~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=620bfde695079eeab35467086956764616de037c;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Render parenthesis around sqlite expression defaults 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) --- diff --git a/doc/build/changelog/unreleased_12/4474.rst b/doc/build/changelog/unreleased_12/4474.rst new file mode 100644 index 0000000000..ba4c6b7c68 --- /dev/null +++ b/doc/build/changelog/unreleased_12/4474.rst @@ -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. diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py index bc7f7fce41..d6a7139880 100644 --- a/lib/sqlalchemy/dialects/sqlite/base.py +++ b/lib/sqlalchemy/dialects/sqlite/base.py @@ -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: diff --git a/test/dialect/test_sqlite.py b/test/dialect/test_sqlite.py index adfca5b534..b9a5c28c1b 100644 --- a/test/dialect/test_sqlite.py +++ b/test/dialect/test_sqlite.py @@ -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))