The Keycloak Upgrade Ledger

Every upgrade we have rehearsed, with the environment stated and the clock running.

2026-09-01 · run 2026-09-01-oracle-s4-dml-only

Oracle S4: a non-owner "DML-only" account cannot run the migration at all

Upgrade
26.0.0 → 26.7.3
Database
Oracle Database Free (23ai/26ai), release 23.26.2.0.0, container gvenzl/oracle-free:23-slim
Topology
single container
Host
laptop (Docker 29, Compose 5.5.0)
Condition
Engines other than Postgres · DB user without DDL rights

Summary

The S4 "DML-only account" row is now closed on the fourth engine. On Oracle it does not just differ in fix — the whole failure mode is structurally different, because the user IS the schema. Two runs:

Conclusion: on Oracle there is no privilege-based fix. The MySQL finding ("DDL privileges suffice") does not translate; neither does the Postgres finding ("ownership required, no GRANT confers DROP INDEX"). On Oracle the account Keycloak connects as must simply be the schema owner — a non-owner DML-only account is not a viable migration posture, because Liquibase cannot see the existing schema through synonyms and cannot create its changelog table beside them.

Environment

FieldValue
Keycloak from → to26.0.0 → 26.7.3
DatabaseOracle Database Free (23ai/26ai), release 23.26.2.0.0, container gvenzl/oracle-free:23-slim
JDBC driverojdbc17 + orai18n 23.26.0.0.0 in fixtures/providers/
Schema ownerkeycloak (DB_DEVELOPER_ROLE, USERS tablespace) — 87 tables, 194 indexes, 0 sequences
Account under testkc_dml — DML grants + private synonyms on all 87 tables (348 grants)
Topologysingle container
Hostlaptop (Docker 29, Compose 5.5.0)
JVM heap-Xms1g -Xmx4g

Procedure

# 26.0.0 schema created by Keycloak as the keycloak owner (87 tables)
./bin/snapshot.sh baseline-26.0.0-oracle-s4

# kc_dml: DML-only account + synonyms (user-is-schema: unqualified names need synonyms)
CREATE USER kc_dml IDENTIFIED BY kc_dml DEFAULT TABLESPACE USERS QUOTA UNLIMITED ON USERS;
GRANT CREATE SESSION TO kc_dml;
-- for each of the 87 tables T owned by KEYCLOAK:
GRANT SELECT,INSERT,UPDATE,DELETE ON keycloak.T TO kc_dml;
CREATE OR REPLACE SYNONYM kc_dml.T FOR keycloak.T;

# run A — DML only
KC_DB_USERNAME=kc_dml ./bin/upgrade.sh 26.7.3 --no-backup     # FAILED, 16s

# run B — DML + ANY-* DDL
GRANT CREATE/ALTER/DROP ANY TABLE|INDEX|SEQUENCE|VIEW|SYNONYM|TRIGGER|PROCEDURE TO kc_dml;
docker compose up -d keycloak                                  # FAILED, 6s

Results

RunAccount rightsOutcomeError
ADML only (SELECT/INSERT/UPDATE/DELETE) + synonymsexit 1, refused to startORA-01031 on CREATE TABLE KC_DML.DATABASECHANGELOG
BDML + full ANY DDL + synonymsexit 1, refused to startORA-00955 on the same CREATE — collides with the synonym

Schema after both runs: keycloak still 26.0.0, 87 tables, 144 changelog rows; kc_dml has 0 tables and 87 synonyms. Neither run modified the owner's schema.

Run A failure

liquibase.exception.SetupException: An error occurred while attempting to create
the database changelog table. ... 'KC_DML.DATABASECHANGELOG'
ORA-01031: insufficient privileges
[Failed SQL: (1031) CREATE TABLE KC_DML.DATABASECHANGELOG (ID VARCHAR2(255) ...)]

Liquibase checks for the changelog table and does not find keycloak.DATABASECHANGELOG, because a synonym is not reported as a table by the catalogue. It concludes the table is missing and tries to create it in the connection's own schema (KC_DML) — a DDL operation a DML-only account cannot perform. Note this is earlier than MySQL, where Liquibase found the existing changelog and only failed trying to ALTER it.

Run B failure

ORA-00955: name is already used by an existing object
[Failed SQL: (955) CREATE TABLE KC_DML.DATABASECHANGELOG (...)]

With CREATE ANY TABLE granted, the same CREATE now clears the privilege check and collides with the synonym kc_dml.DATABASECHANGELOG. A schema cannot hold a table and a synonym of the same name, so the very mechanism that made DML resolve is what blocks Liquibase's DDL.

Verification

ClaimEvidence
DML-only non-owner fails on Oraclerun A ORA-01031 at CREATE TABLE KC_DML.DATABASECHANGELOG
DDL grant does not rescue itrun B ORA-00955 (synonym collision) ☑
Owner schema untouched by both failureskeycloak 26.0.0, 87 tables, 144 changelog rows ☑
Synonyms are the obstaclekc_dml 87 synonyms, 0 tables ☑

← All runs