Search code examples
postgresqlconcurrencytimestamppostgresql-9.3alter-table

Will changing column from timestamp to timestamptz lock the table?


I want to migrate a column from timestamp (no timezone) to timestamptz type.

I'm on Postgres 9.3.9.

I need to know if this operation will cause a table rewrite (lock the table) as my table is large and the db is live.

I found this in the 9.2 release notes:

Increasing the length limit for a varchar or varbit column, or removing the limit altogether, no longer requires a table rewrite. Similarly, increasing the allowable precision of a numeric column, or changing a column from constrained numeric to unconstrained numeric, no longer requires a table rewrite. Table rewrites are also avoided in similar cases involving the interval, timestamp, and timestamptz types.

It sounds promising but doesn't actually detail what 'similar cases' might be.

If this operation is going to lock the table I'd appreciate suggestions for how to work around this on a live db without interrupting service.


Solution

  • First of all, you seem to be confusing locks and table rewrites. The note in the release notes talks about table rewrites - which always take an ACCESS EXCLUSIVE lock on the table. But here are many other operations that also take locks.

    You would need:

    ALTER TABLE tbl ALTER ts_col TYPE timestamptz;
    

    Unless you want to set a specific time zone in the conversion, not the current time zone of your session:

    ALTER TABLE tbl ALTER ts_col TYPE timestamptz USING ts_col AT TIME ZONE 'Europe/London';
    

    Be sure to use a time zone name in this case, not a simple offset nor an abbreviation. Details:

    The manual:

    ALTER TABLE changes the definition of an existing table. There are several subforms described below. Note that the lock level required may differ for each subform. An ACCESS EXCLUSIVE lock is held unless explicitly noted.

    ALTER column_name TYPE data_type takes such an ACCESS EXCLUSIVE lock. While the internal storage format of timestamp and timestamptz are identical, the internal value is typically changed by the conversion (depending on the time zone setting of the session!). Postgres has to write a new version of every row in the table, so this requires a table rewrite as well. Since the operation took an ACCESS EXCLUSIVE lock there is no need to keep old row versions and you will see no dead tuples after the conversion.

    fiddle - demonstrating the role of the time zone setting on the conversion. I also added an example converting varchar to text, which does not require a table rewrite - unless you move to a shorter length modifier.

    Old sqlfiddle - note the output cast to text (ts_col::text) to keep the JDBC layer in sqlfiddle from adding more (unwanted!) representation magic.

    Concurrent transactions trying to access the table after your transaction has started will wait until this transaction has finished.

    You could try to keep the lock time short by preparing a new table in the background, delete the old table and rename the new, but this will make concurrent transactions fail with an error like:

    ERROR: could not open relation with OID 123456

    Details:

    "Similar cases" for timestamp / timestamptz

    Like varchar or numeric timestamp, time and interval types allow modifiers. For example, timestamps store up to 6 digits for fractional seconds per default, but you can modify that: timestamp(0) does not store fractional seconds.

    The conversion from varchar(10) -> varchar(20) does not requires a table rewrite because values in the source type are guaranteed to fit (binary compatible) in the target type as well.

    The same is true for timestamp (0) -> timestamp or timestamptz(3) -> timestamptz(5). That's what the manual refers to in the quoted passage in the release notes:

    Table rewrites are also avoided in similar cases involving the interval, timestamp, and timestamptz types.