Skip to content

Postgres migrations do not respect functional defaults for columns #21627

@kenaniah

Description

@kenaniah

PostgreSQL allows columns to have functional defaults, but Rails does not appear to support this when using the migrations API. The most notorious example of this is outlined below:

CREATE TABLE a ( t1 TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); -- works
CREATE TABLE b ( t1 TIMESTAMPTZ DEFAULT NOW() ); -- works
CREATE TABLE c ( t1 TIMESTAMPTZ DEFAULT 'NOW()' ); -- fails and uses a static timestamp

Tables A and B defined above will work as expected, allowing any new rows to have a proper default value that is calculated at the time of insert. Table C, however, will convert the string 'now' to the current time when the table is defined, causing all inserted default values to reflect the static timestamp instead.

When attempting to use the migration API, one might write code such as:

create_table :c do |t|
  t.timestamp default: 'NOW()'
end

This unfortunately yields the broken use-case of table C from the SQL above.

Proposed Solution

To ensure backwards-compatibility, database portability, and coverage of cases not listed here, I propose adding an option such as raw_default that could be used to pass unquoted defaults to the database in the DDL of a migration.

create_table :c do |t|
  t.timestamp raw_default: 'NOW()'
end

Which would yield the following DDL:

CREATE TABLE c ( t1 TIMESTAMPTZ DEFAULT NOW() );

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions