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
- Install SMF 3.0 Alpha 4 on PostgreSQL and use it a little, so the tables are not empty.
- Run the upgrader.
- 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.
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 storagePRIMARYis not a constraint name in PostgreSQL; dropping a primary key there needs the real constraint name (smf_log_search_results_pkey), orDROP CONSTRAINTlooked up frompg_constraint.2.
Adding support for recurring events3. 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 —— and then hands
not_nullanddefaulttochange_column(), which sets the default and afterwardsSET NOT NULL. In PostgreSQLALTER COLUMN … SET DEFAULTdoes not backfill existing rows, so every existing row is still NULL when the constraint arrives and the statement fails. MySQL is unaffected because itsadd_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).
smf_approval_queuehas no unique index onid_msg, so theignoreinsert method has nothing to conflict on.Steps to reproduce
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.0at bfbca5bDatabase Engine
PostgreSQL
Database Version
PostgreSQL 17
PHP Version
8.4.24
Logs
Additional Information
Found while testing #9517. Point (3) is what makes that PR's
BoardPostsCountmigration 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.