]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Docuemnt current workarounds for MySQL TIMESTAMP
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 29 Jun 2020 18:35:22 +0000 (14:35 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 29 Jun 2020 18:57:47 +0000 (14:57 -0400)
MySQL 8 no longer generates the DEFAULT or ON UPDATE
clauses for TIMESTAMP by default, hence users will begin to hit
this regularly.  add warnings that this non-standard SQL is not
accommodated by server_onupdate and docuemnt the workaround
used in issues such as #4652.

Fixes: #5427
Change-Id: Ie048dcc91c648dd0b80ed395208c1d665b6c968b
(cherry picked from commit a2c2dcaf082ecf97c7e06cd23adf5a614b5a4a2b)

doc/build/core/defaults.rst
lib/sqlalchemy/dialects/mysql/base.py
lib/sqlalchemy/sql/schema.py

index 4d26fc0ef01215f2dd367246df4d7594e91659b5..914eb7b1a033e334131c10b20b0298bab321e926 100644 (file)
@@ -292,6 +292,8 @@ behaviors such as seen with TIMESTAMP columns on some platforms, as well as
 custom triggers that invoke upon INSERT or UPDATE to generate a new value,
 may be called out using :class:`.FetchedValue` as a marker::
 
+    from sqlalchemy.schema import FetchedValue
+
     t = Table('test', meta,
         Column('id', Integer, primary_key=True),
         Column('abc', TIMESTAMP, server_default=FetchedValue()),
@@ -312,6 +314,13 @@ returned.
 For details on using :class:`.FetchedValue` with the ORM, see
 :ref:`orm_server_defaults`.
 
+.. warning:: The :paramref:`_schema.Column.server_onupdate` directive
+    **does not** currently produce MySQL's
+    "ON UPDATE CURRENT_TIMESTAMP()" clause.  See
+    :ref:`mysql_timestamp_onupdate` for background on how to produce
+    this clause.
+
+
 .. seealso::
 
     :ref:`orm_server_defaults`
index 12156a1de67108f51e52790347c78a3395a3e6af..0b4f7c7286f0339abd6c867d1e05a9e47dc3035b 100644 (file)
@@ -670,10 +670,76 @@ with the ``unique=True`` setting present in the :attr:`_schema.Table.indexes`
 collection.
 
 
+TIMESTAMP issues
+-----------------
+
+.. _mysql_timestamp_onupdate:
+
+Rendering ON UPDATE CURRENT TIMESTAMP for MySQL's explicit_defaults_for_timestamp
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+MySQL has historically expanded the DDL for the :class:`_types.TIMESTAMP`
+datatype into the phrase "TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE
+CURRENT_TIMESTAMP", which includes non-standard SQL that automatically updates
+the column with the current timestamp when an UPDATE occurs, eliminating the
+usual need to use a trigger in such a case where server-side update changes are
+desired.
+
+MySQL 5.6 introduced a new flag `explicit_defaults_for_timestamp
+<http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html
+#sysvar_explicit_defaults_for_timestamp>`_ which disables the above behavior,
+and in MySQL 8 this flag defaults to true, meaning in order to get a MySQL
+"on update timestamp" without changing this flag, the above DDL must be
+rendered explicitly.
+
+SQLAlchemy's MySQL dialect does not yet have an option to generate
+MySQL's "ON UPDATE CURRENT_TIMESTAMP" clause, noting that this is not a general
+purpose "ON UPDATE" as there is no such syntax in standard SQL.  SQLAlchemy's
+:paramref:`_schema.Column.server_onupdate` parameter is currently not related
+to this special MySQL behavior.
+
+To generate this DDL, make use of the :paramref:`_schema.Column.server_default`
+parameter and pass a textual clause that also includes the ON UPDATE clause::
+
+    from sqlalchemy import Table, MetaData, Column, Integer, String, TIMESTAMP
+    from sqlalchemy import text
+
+    metadata = MetaData()
+
+    mytable = Table(
+        "mytable",
+        metadata,
+        Column('id', Integer, primary_key=True),
+        Column('data', String(50)),
+        Column(
+            'last_updated',
+            TIMESTAMP,
+            server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
+        )
+    )
+
+Even though the :paramref:`_schema.Column.server_onupdate` feature does not
+generate this DDL, it still may be desirable to signal to the ORM that this
+updated value should be fetched.  This syntax looks like the following::
+
+    from sqlalchemy.schema import FetchedValue
+
+    class MyClass(Base):
+        __tablename__ = 'mytable'
+
+        id = Column(Integer, primary_key=True)
+        data = Column(String(50))
+        last_updated = Column(
+            TIMESTAMP,
+            server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
+            server_onupdate=FetchedValue()
+        )
+
+
 .. _mysql_timestamp_null:
 
 TIMESTAMP Columns and NULL
---------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 MySQL historically enforces that a column which specifies the
 TIMESTAMP datatype implicitly includes a default value of
index 897beaf94fdef3511ff7b1ac4220088d89d6f59a..2d8097114a3a9c7381b92dfaffc2ab9b85566ec4 100644 (file)
@@ -1330,6 +1330,12 @@ class Column(DialectKWArgs, SchemaItem, ColumnClause):
             implement any kind of generation function within the database,
             which instead must be specified separately.
 
+
+            .. warning:: This directive **does not** currently produce MySQL's
+               "ON UPDATE CURRENT_TIMESTAMP()" clause.  See
+               :ref:`mysql_timestamp_onupdate` for background on how to
+               produce this clause.
+
             .. seealso::
 
                 :ref:`triggered_columns`