Skip to content

[3.0]: PostgreSQL: the upgrader cannot finish, and unapproving a post errors #9519

Description

@albertlast

Basic Information

A forum installed on PostgreSQL cannot be brought up to date: the upgrader stops on the first of several statements that are either MySQL-only or illegal against a table that already has rows. Each run gets a little further, because the migrations in front partially apply, and then dies on the next one.

1. Improving search results storage

ERROR:  syntax error at or near "PRIMARY" at character 57
STATEMENT:  ALTER TABLE smf_log_search_results
                        DROP CONSTRAINT PRIMARY

PRIMARY is not a constraint name in PostgreSQL; dropping a primary key there needs the real constraint name (smf_log_search_results_pkey), or DROP CONSTRAINT looked up from pg_constraint.

2. Adding support for recurring events

ERROR:  column "end_time" of relation "smf_calendar" contains null values
STATEMENT:  ALTER TABLE smf_calendar
                ALTER COLUMN end_time SET NOT NULL

3. The general case behind (2): adding a NOT NULL column with a default to a table that already has rows.

PostgreSQL::add_column() emits the column bare —

'ALTER TABLE ' . $short_table_name . '
ADD COLUMN ' . $column_info['name'] . ' ' . $type . $generated,

— and then hands not_null and default to change_column(), which sets the default and afterwards SET NOT NULL. In PostgreSQL ALTER COLUMN … SET DEFAULT does not backfill existing rows, so every existing row is still NULL when the constraint arrives and the statement fails. MySQL is unaffected because its add_column() emits the whole definition in one statement.

Anything that adds such a column is therefore broken on PostgreSQL, and it leaves the table half-changed: the new column exists, nullable and empty, and the migration is not re-runnable — a second attempt fails at exactly the same statement.

4. Unapproving a post fails at runtime (not the upgrader).

ERROR:  there is no unique or exclusion constraint matching the ON CONFLICT specification
STATEMENT:  INSERT INTO smf_approval_queue("id_msg")
                    VALUES
                        (13) ON CONFLICT (id_msg) DO NOTHING

smf_approval_queue has no unique index on id_msg, so the ignore insert method has nothing to conflict on.

Steps to reproduce

  1. Install SMF 3.0 Alpha 4 on PostgreSQL and use it a little, so the tables are not empty.
  2. Run the upgrader.
  3. Run it again after it stops.

For (4): turn post moderation on, then unapprove a reply from the topic view.

Expected result

The upgrader walks through every migration and finishes. Unapproving a post queues it.

Actual result

The upgrader stops at the first offending statement, and re-running does not get past it. Unapproving a post writes a database error to the log.

Version/Git revision

3.0 Alpha 4, release-3.0 at bfbca5b

Database Engine

PostgreSQL

Database Version

PostgreSQL 17

PHP Version

8.4.24

Logs

2026-08-14 11:54:34 UTC ERROR:  syntax error at or near "PRIMARY" at character 57
2026-08-14 11:54:34 UTC STATEMENT:  ALTER TABLE smf_log_search_results
                        DROP CONSTRAINT PRIMARY
2026-08-14 11:54:45 UTC ERROR:  column "end_time" of relation "smf_calendar" contains null values
2026-08-14 11:54:45 UTC STATEMENT:  ALTER TABLE smf_calendar
                ALTER COLUMN end_time SET NOT NULL
2026-08-14 12:00:31 UTC ERROR:  there is no unique or exclusion constraint matching the ON CONFLICT specification
2026-08-14 12:00:31 UTC STATEMENT:  INSERT INTO smf_approval_queue("id_msg")
                    VALUES
                        (13) ON CONFLICT (id_msg) DO NOTHING

Additional Information

Found while testing #9517. Point (3) is what makes that PR's BoardPostsCount migration fail on PostgreSQL as well; I have noted it on the PR, but the root cause is in the schema layer rather than in any one migration, so fixing it here would fix that one too.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions