Skip to content

Remove hardcoded DEFINER clauses so migrations run as any DB user - #142

Open
drtechie wants to merge 1 commit into
mainfrom
fix/portable-definer
Open

Remove hardcoded DEFINER clauses so migrations run as any DB user#142
drtechie wants to merge 1 commit into
mainfrom
fix/portable-definer

Conversation

@drtechie

@drtechie drtechie commented Jul 26, 2026

Copy link
Copy Markdown
Member

Nine DEFINER clauses across eight migrations pin the object owner to a specific account. This makes a fresh deployment fail unless the database user happens to be named piramaldev, and dbiemr/V62 cannot be applied by any non-root account at all.

migration(s) clause
dbiemr V13, V32, V34, V35, V71, V83 DEFINER=`piramaldev`@`%`
dbiemr V62 DEFINER=`root`@`localhost`
db1097identity V1 (×2) DEFINER=`root`@`localhost`

Why it breaks

MySQL only lets an account create an object whose DEFINER equals that account, unless it holds SET_USER_ID. Since 8.0.16 root@localhost is additionally a system account, so attributing an object to it also requires SYSTEM_USER — which is close to root-equivalent and not something an application account should hold.

Because FlywayMigrator runs in @PostConstruct, the failure presents as amrit-db crash-looping rather than as a clean migration error, which makes it expensive to diagnose.

Measured on MySQL 8.0.46, as an ordinary unprivileged app user

form result
DEFINER=`root`@`localhost` ERROR 1227 — needs SYSTEM_USER
DEFINER=`otherpartner`@`%` ERROR 1227 — needs SET_USER_ID
DEFINER=`piramaldev`@`%` OK, but only because that is the connecting account
no DEFINER clause (this PR) OK with zero elevated privileges

The change

Removing the clause makes MySQL default the definer to CURRENT_USER — whichever account runs the migration. Verified: the resulting objects record definer = <migrating user>.

SQL SECURITY DEFINER is preserved everywhere it appeared, so the privilege semantics of each view and routine are unchanged. Only the identity it binds to changes, from a hardcoded name to the actual owner.

Diff is 6 insertions / 9 deletions. Three shapes were handled separately to keep each edit minimal:

  • multi-line view headers (CREATE / ALGORITHM / DEFINER / SQL SECURITY DEFINER)
  • inline CREATE DEFINER=`u`@`h` PROCEDURE
  • mysqldump /*!50013 ... */ version-gated comments

Why this is safe for existing deployments

FlywayMigrator.migrate() calls .repair() on all four Flyway instances before .migrate(), which realigns checksums in flyway_schema_history on every startup — so editing an applied migration does not cause a checksum mismatch. And on existing deployments these migrations have already been applied, so they will not re-run at all.

Secondary benefit

A hardcoded DEFINER is also a latent runtime fault: an object whose definer does not exist on the target server fails when queried with The user specified as a definer does not exist. That surfaces as a broken screen rather than a failed deployment, long after the fact.

How this was found

Standing up AMRIT MMU for a new partner on a clean Ubuntu 24.04 / MySQL 8.0 host with a per-partner database user. Without this change the deployment needs the DB user renamed to piramaldev and a temporary SYSTEM_USER grant during migration; with it, neither is required.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Updated database views and stored procedures to avoid relying on hard-coded execution-owner accounts.
    • Preserved existing view results, procedure parameters, and business logic.
    • Improved migration compatibility across environments with different database account configurations.

Nine DEFINER clauses across eight migrations pin the object owner to a specific
account. That makes a fresh deployment fail unless the database user happens to
be named `piramaldev`, and V62 cannot be applied by any non-root account at all.

  dbiemr V13, V32, V34, V35, V71, V83   DEFINER=`piramaldev`@`%`
  dbiemr V62                            DEFINER=`root`@`localhost`
  db1097identity V1  (x2)               DEFINER=`root`@`localhost`

MySQL only lets an account create an object whose DEFINER equals that account,
unless it holds SET_USER_ID. Since 8.0.16 `root@localhost` is additionally a
system account, so attributing an object to it also requires SYSTEM_USER — which
is close to root-equivalent and not something an application account should hold.

Measured on MySQL 8.0.46 as an ordinary unprivileged app user:

  DEFINER=`root`@`localhost`     -> ERROR 1227, needs SYSTEM_USER
  DEFINER=`otherpartner`@`%`     -> ERROR 1227, needs SET_USER_ID
  DEFINER=`piramaldev`@`%`       -> OK, but only because that IS the account
  no DEFINER clause (this PR)    -> OK with zero elevated privileges

Removing the clause makes MySQL default the definer to CURRENT_USER, i.e.
whichever account runs the migration. Verified: the resulting objects record
definer = the migrating user. SQL SECURITY DEFINER is preserved everywhere it
appeared, so the privilege semantics of each view and routine are unchanged —
only the identity it binds to changes, from a hardcoded name to the actual owner.

Editing applied migrations is safe here because FlywayMigrator.migrate() calls
repair() on all four instances before migrate(), which realigns checksums in
flyway_schema_history on every startup. Existing deployments are unaffected:
these migrations have already been applied there and will not re-run.

Beyond the create-time failure, a hardcoded DEFINER is a latent runtime fault:
an object whose definer does not exist on the target server fails when queried
with "The user specified as a definer does not exist", which surfaces as a
broken screen rather than a failed deployment.

Diff is 6 insertions and 9 deletions; three shapes were handled separately to
keep each edit minimal — multi-line view headers, inline CREATE DEFINER=...
PROCEDURE, and mysqldump's /*!50013 ... */ version-gated comments.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8cf09a80-93c3-4448-a445-0906f8c1886b

📥 Commits

Reviewing files that changed from the base of the PR and between 368e374 and ba49d0e.

📒 Files selected for processing (8)
  • src/main/resources/db/migration/db1097identity/V1__DB_1097_IDENTITY.sql
  • src/main/resources/db/migration/dbiemr/V13__QUALITY_MODULE_CATEGORY_MAPPING.sql
  • src/main/resources/db/migration/dbiemr/V32__Assam_SP.sql
  • src/main/resources/db/migration/dbiemr/V34__SP_ECD.sql
  • src/main/resources/db/migration/dbiemr/V35__ECD_Child_SP.sql
  • src/main/resources/db/migration/dbiemr/V62__DB_v_drugforprescription.sql
  • src/main/resources/db/migration/dbiemr/V71__ECD_Mother_HighRisk_Reason_SP.sql
  • src/main/resources/db/migration/dbiemr/V83__DB_view_correction.sql
💤 Files with no reviewable changes (3)
  • src/main/resources/db/migration/dbiemr/V62__DB_v_drugforprescription.sql
  • src/main/resources/db/migration/dbiemr/V83__DB_view_correction.sql
  • src/main/resources/db/migration/dbiemr/V13__QUALITY_MODULE_CATEGORY_MAPPING.sql

📝 Walkthrough

Walkthrough

The migrations remove explicit MySQL DEFINER clauses from eight views and stored procedures. Existing view security modes, selected columns, procedure signatures, and procedure bodies remain unchanged.

Changes

SQL definer metadata updates

Layer / File(s) Summary
View definition metadata
src/main/resources/db/migration/db1097identity/V1__DB_1097_IDENTITY.sql, src/main/resources/db/migration/dbiemr/V13__QUALITY_MODULE_CATEGORY_MAPPING.sql, src/main/resources/db/migration/dbiemr/V62__DB_v_drugforprescription.sql, src/main/resources/db/migration/dbiemr/V83__DB_view_correction.sql
View creation statements remove explicit DEFINER identities while retaining existing view logic and SQL SECURITY DEFINER settings.
Procedure definition metadata
src/main/resources/db/migration/dbiemr/V32__Assam_SP.sql, src/main/resources/db/migration/dbiemr/V34__SP_ECD.sql, src/main/resources/db/migration/dbiemr/V35__ECD_Child_SP.sql, src/main/resources/db/migration/dbiemr/V71__ECD_Mother_HighRisk_Reason_SP.sql
Stored procedure declarations remove explicit DEFINER clauses while preserving names, parameters, and bodies.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related PRs

Suggested reviewers: paras-dba

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: removing hardcoded DEFINER clauses to make migrations runnable by ordinary DB users.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant