Skip to content

fixing migration issues#95

Open
ramnar wants to merge 67 commits intoPSMRI:mainfrom
ramnar:bugfix/migration
Open

fixing migration issues#95
ramnar wants to merge 67 commits intoPSMRI:mainfrom
ramnar:bugfix/migration

Conversation

@ramnar
Copy link

@ramnar ramnar commented Feb 18, 2026

📋 Description

JIRA ID:

Please provide a summary of the change and the motivation behind it. Include relevant context and details.


✅ Type of Change

  • 🐞 Bug fix (non-breaking change which resolves an issue)
  • New feature (non-breaking change which adds functionality)
  • 🔥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 🛠 Refactor (change that is neither a fix nor a new feature)
  • ⚙️ Config change (configuration file or build script updates)
  • 📚 Documentation (updates to docs or readme)
  • 🧪 Tests (adding new or updating existing tests)
  • 🎨 UI/UX (changes that affect the user interface)
  • 🚀 Performance (improves performance)
  • 🧹 Chore (miscellaneous changes that don't modify src or test files)

ℹ️ Additional Information

Please describe how the changes were tested, and include any relevant screenshots, logs, or other information that provides additional context.

Summary by CodeRabbit

  • Bug Fixes

    • Improved migration resilience with better error handling, automatic recovery from validation failures, and detailed migration logging.
  • Chores

    • Made schema migrations safe and idempotent by adding existence checks to avoid duplicate operations and reduce deployment failures.
    • Consolidated datasource/migration configuration for more reliable deploys.
  • New Features

    • Added multiple new tables, stored procedures, and indexes to support reporting, forms, campaign tracking, and sync/error-tracking.

@coderabbitai
Copy link

coderabbitai bot commented Feb 18, 2026

📝 Walkthrough

Walkthrough

Disables Flyway validation on migrate for four datasource beans and centralizes guarded migration logic with repair-and-retry handling. Many SQL migrations were converted to idempotent conditional DDL (INFORMATION_SCHEMA checks / dynamic PREPARE/EXECUTE) and several new/modified migration scripts were added.

Changes

Cohort / File(s) Summary
Flyway configuration & orchestrator
src/main/java/com/db/piramalswasthya/config/FlywayConfig.java, src/main/java/com/db/piramalswasthya/config/FlywayMigrator.java
Disabled validateOnMigrate(false) on four Flyway beans; implemented migrateDatabase(Flyway, String) to wrap per-db migrations, handle FlywayValidateException by running repair() and retrying, and add structured logging and error propagation.
Idempotent db_identity migrations
src/main/resources/db/migration/dbidentity/V1__DB_IDENTITY.sql, src/main/resources/db/migration/dbidentity/V3__DB_IDENTITY.sql, src/main/resources/db/migration/dbidentity/V7__ELASTICSEARCH.sql, src/main/resources/db/migration/dbidentity/V8__DB_identity_children_columns.sql, src/main/resources/db/migration/dbidentity/V9__DB_isSpoupseadd_columns.sql
Converted many DDL/session-control lines to commented form and added INFORMATION_SCHEMA guarded ALTERs / conditional column additions; added t_elasticsearch_sync_job table migration.
Idempotent & new db_iemr migrations (bulk)
src/main/resources/db/migration/dbiemr/*
Large set of db_iemr migrations: converted unconditional ALTER/INDEX/CONSTRAINT statements to idempotent guarded blocks, added many new tables, stored procedures, columns, indexes, and schema modifications (V2, V9, V12, V13, V14, V17, V22–V60, etc.). Many scripts use dynamic SQL with PREPARE/EXECUTE/DEALLOCATE and INFORMATION_SCHEMA checks.
1097 identity dump adjustments
src/main/resources/db/migration/db1097identity/V1__DB_1097_IDENTITY.sql
Commented out numerous session/dump statements (SET/FOREIGN_KEY_CHECKS/SQL_MODE/etc.), preserving original content as comments and reducing executed session configuration commands.
Environment & docs
src/main/environment/common_docker.properties, README.md
Added common_docker.properties with datasource placeholders for four DBs; added a DeepWiki badge to README.md.

Sequence Diagram

sequenceDiagram
    participant App as Application
    participant FM as FlywayMigrator
    participant FW as Flyway
    participant DB as Database

    App->>FM: migrate()
    loop per-database
        FM->>FM: migrateDatabase(flyway, dbName) / log start
        FM->>FW: flyway.migrate()
        FW->>DB: apply migrations
        alt migrations succeed
            FW-->>FM: success
            FM->>FM: log success
        else FlywayValidateException
            FW-->>FM: validation error
            FM->>FM: log validation error
            FM->>FW: flyway.repair()
            FW->>DB: repair metadata
            FW-->>FM: repair complete
            FM->>FW: flyway.migrate() (retry)
            FW->>DB: apply migrations
            FW-->>FM: success / failure
            FM->>FM: log final outcome
        end
    end
    FM->>App: return / rethrow
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • paras-dba
  • drtechie

Poem

🐰 Hopped through schemas, neat and spry,
I checked each column, watchful eye.
If validation stumbles, I repair and try,
Then hop along—migrations fly! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'fixing migration issues' is vague and generic, failing to convey specific information about the changeset despite substantial modifications across 50+ migration files. Replace with a more descriptive title that identifies the primary scope, such as 'Add idempotent migration guards and new schema tables' or 'Refactor Flyway configuration and consolidate database migrations'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
src/main/java/com/db/piramalswasthya/config/FlywayMigrator.java (3)

12-12: Declare logger as static final

Non-static logger fields create a separate instance per object. Standard SLF4J practice is private static final Logger.

♻️ Proposed fix
-private Logger logger = LoggerFactory.getLogger(FlywayMigrator.class);
+private static final Logger logger = LoggerFactory.getLogger(FlywayMigrator.class);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/db/piramalswasthya/config/FlywayMigrator.java` at line 12,
The logger field in FlywayMigrator is declared as an instance field; change the
declaration of logger (Logger logger in class FlywayMigrator) to a static final
field (private static final Logger) so a single SLF4J logger instance is used
per class; update the declaration only (no behavior change) and ensure imports
remain valid.

45-56: Use SLF4J parameterized logging instead of string concatenation

String concatenation evaluates eagerly regardless of whether the log level is enabled, causing unnecessary allocations on hot paths.

♻️ Proposed refactor
-logger.info("Starting migration for database: " + dbName);
+logger.info("Starting migration for database: {}", dbName);

-logger.info("Successfully migrated database: " + dbName);
+logger.info("Successfully migrated database: {}", dbName);

-logger.warn("Validation error for " + dbName + ": " + e.getMessage() + ". Attempting repair...");
+logger.warn("Validation error for {}: {}. Attempting repair...", dbName, e.getMessage());

-logger.info("Repair completed for " + dbName + ". Retrying migration...");
+logger.info("Repair completed for {}. Retrying migration...", dbName);

-logger.info("Successfully migrated database after repair: " + dbName);
+logger.info("Successfully migrated database after repair: {}", dbName);

-logger.error("Failed to repair and migrate " + dbName + ": " + repairException.getMessage(), repairException);
+logger.error("Failed to repair and migrate {}: {}", dbName, repairException.getMessage(), repairException);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/db/piramalswasthya/config/FlywayMigrator.java` around lines
45 - 56, Replace all string-concatenation logging calls in FlywayMigrator (e.g.,
logger.info("Starting migration for database: " + dbName),
logger.warn("Validation error for " + dbName + ": " + e.getMessage() + ".
Attempting repair..."), logger.info("Repair completed for " + dbName + ".
Retrying migration..."), logger.error("Failed to repair and migrate " + dbName +
": " + repairException.getMessage(), repairException)) with SLF4J parameterized
logging (use placeholders like {} and pass dbName and exception objects as
arguments) so message construction is deferred when the log level is disabled
and exceptions are logged via the throwable parameter rather than concatenating
getMessage().

31-34: Fail-fast cascade: a single DB migration failure silently skips all remaining databases

If migrateDatabase(flywayDbiemr, ...) throws, execution never reaches flywayDbidentity, flywayDbreporting, or flywayDb1097identity. In environments where one schema has stale state but others are healthy, this leaves every downstream database un-migrated on that startup cycle, potentially widening the outage scope.

Consider whether a collect-all-errors approach (migrate all four regardless of earlier failures, then throw an aggregate exception) better suits the deployment requirements here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/db/piramalswasthya/config/FlywayMigrator.java` around lines
31 - 34, The current sequential calls in FlywayMigrator
(migrateDatabase(flywayDbiemr, "db_iemr"), migrateDatabase(flywayDbidentity,
"db_identity"), migrateDatabase(flywayDbreporting, "db_reporting"),
migrateDatabase(flywayDb1097identity, "db_1097_identity")) fail fast and abort
remaining migrations on the first exception; change this to a collect-all-errors
pattern: invoke migrateDatabase for each of the four Flyway instances
(flywayDbiemr, flywayDbidentity, flywayDbreporting, flywayDb1097identity) inside
individual try/catch blocks, accumulate any thrown Exceptions (or their
messages) into a list, and after attempting all four, if the list is non-empty
throw an aggregated exception (or a new RuntimeException containing all
messages) so callers see every failure rather than only the first. Ensure you
reference the migrateDatabase method and preserve existing logging behavior
while adding the aggregation/throw at the end.
src/main/resources/db/migration/dbiemr/V9__1097Subcategorychanges.sql (1)

1-10: Remove leftover dead code — duplicate USE and commented-out legacy statements.

Lines 1–6 are remnants of the pre-refactoring version of this file. Line 1's use db_iemr; and the commented-out ALTER TABLE statements at lines 3–6 are superseded entirely by the new guarded block starting at line 10.

🧹 Proposed cleanup
-use db_iemr;
-
---  ALTER TABLE t_kmfilemanager ADD COLUMN SubCategoryID INT NULL;
-
--- ALTER TABLE t_kmfilemanager ADD CONSTRAINT FK_KmFileManager_SubCategory
---   FOREIGN KEY (SubCategoryID) REFERENCES m_subcategory(SubCategoryID);
-
-
-
 USE db_iemr;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/db/migration/dbiemr/V9__1097Subcategorychanges.sql` around
lines 1 - 10, Remove the leftover duplicate "USE db_iemr;" and the commented-out
legacy ALTER TABLE statements for t_kmfilemanager (the commented lines
referencing SubCategoryID, FK_KmFileManager_SubCategory and m_subcategory); keep
only the intended guarded/updated migration block and delete those dead lines so
the file contains one USE statement and no obsolete commented schema changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/com/db/piramalswasthya/config/FlywayConfig.java`:
- Line 18: Remove the validateOnMigrate(false) call from all Flyway bean
builders in FlywayConfig (the beans for flyway, flywayDbidentity,
flywayDbreporting, and flywayDb1097identity) so Flyway's validation runs during
migrate() and allows FlywayValidateException to be thrown and handled by
FlywayMigrator.migrateDatabase; if you need to tolerate specific
missing/modified scripts instead of disabling validation entirely, configure
ignoreMigrationPatterns on the respective Flyway bean(s) rather than calling
validateOnMigrate(false).

In `@src/main/java/com/db/piramalswasthya/config/FlywayMigrator.java`:
- Line 35: In FlywayMigrator remove the debug System.out.println("SUCCESS");
statement (it duplicates the existing logger.info call) so only the logger is
used for success messages; locate the println in the FlywayMigrator class (near
the method performing migration) and delete that line, leaving the existing
logger.info(...) intact for consistent logging.

In `@src/main/resources/db/migration/dbiemr/V9__1097Subcategorychanges.sql`:
- Around line 13-17: The guard queries that check information_schema hardcode
table_schema = 'db_iemr', which breaks idempotency if the migration runs in a
differently named database; update the two checks that reference
information_schema (the one checking column_name = 'SubCategoryID' on table
t_kmfilemanager and the FK existence check) to use DATABASE() instead of the
literal 'db_iemr' so they inspect the current schema dynamically, keeping the
rest of the logic (t_kmfilemanager, SubCategoryID, and the FK name checks)
unchanged.
- Line 21: Replace the fallback SQL literals that use double quotes with
single-quoted strings escaped for embedding: find the occurrences of the literal
'SELECT "Column SubCategoryID already exists";' (and the similar fallback at the
other occurrence) in V9__1097Subcategorychanges.sql and change them to use
escaped single quotes for the inner string (i.e., embed single quotes by
doubling them) so the PREPARE/EXECUTE path becomes safe under ANSI_QUOTES;
update both occurrences accordingly.

---

Nitpick comments:
In `@src/main/java/com/db/piramalswasthya/config/FlywayMigrator.java`:
- Line 12: The logger field in FlywayMigrator is declared as an instance field;
change the declaration of logger (Logger logger in class FlywayMigrator) to a
static final field (private static final Logger) so a single SLF4J logger
instance is used per class; update the declaration only (no behavior change) and
ensure imports remain valid.
- Around line 45-56: Replace all string-concatenation logging calls in
FlywayMigrator (e.g., logger.info("Starting migration for database: " + dbName),
logger.warn("Validation error for " + dbName + ": " + e.getMessage() + ".
Attempting repair..."), logger.info("Repair completed for " + dbName + ".
Retrying migration..."), logger.error("Failed to repair and migrate " + dbName +
": " + repairException.getMessage(), repairException)) with SLF4J parameterized
logging (use placeholders like {} and pass dbName and exception objects as
arguments) so message construction is deferred when the log level is disabled
and exceptions are logged via the throwable parameter rather than concatenating
getMessage().
- Around line 31-34: The current sequential calls in FlywayMigrator
(migrateDatabase(flywayDbiemr, "db_iemr"), migrateDatabase(flywayDbidentity,
"db_identity"), migrateDatabase(flywayDbreporting, "db_reporting"),
migrateDatabase(flywayDb1097identity, "db_1097_identity")) fail fast and abort
remaining migrations on the first exception; change this to a collect-all-errors
pattern: invoke migrateDatabase for each of the four Flyway instances
(flywayDbiemr, flywayDbidentity, flywayDbreporting, flywayDb1097identity) inside
individual try/catch blocks, accumulate any thrown Exceptions (or their
messages) into a list, and after attempting all four, if the list is non-empty
throw an aggregated exception (or a new RuntimeException containing all
messages) so callers see every failure rather than only the first. Ensure you
reference the migrateDatabase method and preserve existing logging behavior
while adding the aggregation/throw at the end.

In `@src/main/resources/db/migration/dbiemr/V9__1097Subcategorychanges.sql`:
- Around line 1-10: Remove the leftover duplicate "USE db_iemr;" and the
commented-out legacy ALTER TABLE statements for t_kmfilemanager (the commented
lines referencing SubCategoryID, FK_KmFileManager_SubCategory and
m_subcategory); keep only the intended guarded/updated migration block and
delete those dead lines so the file contains one USE statement and no obsolete
commented schema changes.

.dataSource(dataSource)
.locations("classpath:db/migration/dbiemr")
.baselineOnMigrate(true)
.validateOnMigrate(false)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

validateOnMigrate(false) silently disables checksum integrity AND makes the FlywayValidateException repair logic in FlywayMigrator unreachable dead code

FlywayValidateException is thrown inside migrate() only when the internal doValidate() step runs — which is gated on validateOnMigrate. With it set to false on all four beans, flyway.migrate() will never throw FlywayValidateException, so the entire catch (org.flywaydb.core.api.exception.FlywayValidateException e) block in FlywayMigrator.migrateDatabase() — including the repair-and-retry path — is dead code that will never execute.

Additionally, validateOnMigrate verifies each migration's checksum to confirm it has not changed after being applied to the database. Disabling it permanently means a tampered or accidentally modified applied script will silently pass at startup.

The intent of this PR is clearly to repair-and-retry on validation failure. The correct approach is to remove validateOnMigrate(false) so that validation runs normally during migrate(), allowing FlywayValidateException to be thrown and caught by the handler in FlywayMigrator. If certain known-missing scripts need to be tolerated without a full disable, prefer ignoreMigrationPatterns.

🐛 Proposed fix — remove validateOnMigrate(false) from all four beans
  return Flyway.configure()
          .dataSource(dataSource)
          .locations("classpath:db/migration/dbiemr")
          .baselineOnMigrate(true)
-         .validateOnMigrate(false)
          .load();

Apply the same removal to flywayDbidentity, flywayDbreporting, and flywayDb1097identity.

Also applies to: 28-28, 38-38, 48-48

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/db/piramalswasthya/config/FlywayConfig.java` at line 18,
Remove the validateOnMigrate(false) call from all Flyway bean builders in
FlywayConfig (the beans for flyway, flywayDbidentity, flywayDbreporting, and
flywayDb1097identity) so Flyway's validation runs during migrate() and allows
FlywayValidateException to be thrown and handled by
FlywayMigrator.migrateDatabase; if you need to tolerate specific
missing/modified scripts instead of disabling validation entirely, configure
ignoreMigrationPatterns on the respective Flyway bean(s) rather than calling
validateOnMigrate(false).

migrateDatabase(flywayDbidentity, "db_identity");
migrateDatabase(flywayDbreporting, "db_reporting");
migrateDatabase(flywayDb1097identity, "db_1097_identity");
System.out.println("SUCCESS");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove debug System.out.println

Line 35 is a debug artifact that duplicates the logger.info on line 36.

🐛 Proposed fix
-            System.out.println("SUCCESS");
             logger.info("Flyway migration completed successfully");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
System.out.println("SUCCESS");
logger.info("Flyway migration completed successfully");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/db/piramalswasthya/config/FlywayMigrator.java` at line 35,
In FlywayMigrator remove the debug System.out.println("SUCCESS"); statement (it
duplicates the existing logger.info call) so only the logger is used for success
messages; locate the println in the FlywayMigrator class (near the method
performing migration) and delete that line, leaving the existing
logger.info(...) intact for consistent logging.

Comment on lines 13 to 17
SELECT COUNT(*) INTO @col_exists
FROM information_schema.columns
WHERE table_schema = 'db_iemr'
AND table_name = 't_kmfilemanager'
AND column_name = 'SubCategoryID';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded table_schema = 'db_iemr' makes the guard brittle.

Both information_schema checks (lines 13–17 for the column, lines 28–33 for the FK) hardcode the schema name as 'db_iemr'. If this migration ever runs against an instance where the schema is named differently (e.g., a dev or staging environment with a different naming convention), the check will always return 0, causing the migration to unconditionally attempt the DDL. On a second run this would fail with a "duplicate column" or "duplicate key name" error, defeating the idempotency goal.

Use DATABASE() to reference the current schema dynamically instead:

♻️ Proposed fix using `DATABASE()`
 SELECT COUNT(*) INTO `@col_exists`
 FROM information_schema.columns
-WHERE table_schema = 'db_iemr'
+WHERE table_schema = DATABASE()
   AND table_name = 't_kmfilemanager'
   AND column_name = 'SubCategoryID';
 SELECT COUNT(*) INTO `@fk_exists`
 FROM information_schema.table_constraints
-WHERE table_schema = 'db_iemr'
+WHERE table_schema = DATABASE()
   AND table_name = 't_kmfilemanager'
   AND constraint_type = 'FOREIGN KEY'
   AND constraint_name = 'FK_KmFileManager_SubCategory';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
SELECT COUNT(*) INTO @col_exists
FROM information_schema.columns
WHERE table_schema = 'db_iemr'
AND table_name = 't_kmfilemanager'
AND column_name = 'SubCategoryID';
SELECT COUNT(*) INTO `@col_exists`
FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = 't_kmfilemanager'
AND column_name = 'SubCategoryID';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/db/migration/dbiemr/V9__1097Subcategorychanges.sql` around
lines 13 - 17, The guard queries that check information_schema hardcode
table_schema = 'db_iemr', which breaks idempotency if the migration runs in a
differently named database; update the two checks that reference
information_schema (the one checking column_name = 'SubCategoryID' on table
t_kmfilemanager and the FK existence check) to use DATABASE() instead of the
literal 'db_iemr' so they inspect the current schema dynamically, keeping the
rest of the logic (t_kmfilemanager, SubCategoryID, and the FK name checks)
unchanged.

SET @sql := IF(
@col_exists = 0,
'ALTER TABLE t_kmfilemanager ADD COLUMN SubCategoryID INT NULL;',
'SELECT "Column SubCategoryID already exists";'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

MySQL ANSI_QUOTES sql_mode behavior double quotes PREPARE EXECUTE

💡 Result:

With sql_mode including ANSI_QUOTES, MySQL treats double quotes (") as identifier quotes (like backticks), not as string-literal quotes. That applies equally to statements executed normally and statements prepared via PREPARE/EXECUTE. So:

  • "col" means the identifier col, not the string "col"; string literals must use single quotes ('...'). [1][2]
  • Example (fails under ANSI_QUOTES because "abc" is parsed as an identifier):
    SET SESSION sql_mode = 'ANSI_QUOTES';
    PREPARE s FROM 'SELECT "abc"';   -- interpreted as SELECT <identifier abc>
    EXECUTE s;                       -- typically errors: unknown column 'abc'
    Correct form:
    PREPARE s FROM 'SELECT ''abc'''; -- or: PREPARE s FROM "SELECT 'abc'";

When does it take effect for prepared statements?
The SQL text is parsed/compiled at PREPARE time (for example, parameter types are determined when first prepared and then retained across EXECUTE calls). So the session’s sql_mode (including ANSI_QUOTES) needs to be set before PREPARE for it to influence how double quotes are interpreted inside the prepared SQL text. [3]

Still need a literal " character inside a string under ANSI_QUOTES?
Put it inside a single-quoted string literal, e.g. 'He said "hi"' (the " is just a character there). [2]

Sources: MySQL identifier quoting / ANSI_QUOTES behavior [1], string literal rules under ANSI_QUOTES [2], PREPARE compilation-time behavior (parameters typed when first prepared) [3].

[1] MySQL Manual: Identifiers and ANSI_QUOTES (double quotes quote identifiers; can’t quote strings)
[2] MySQL Manual: String literals (double-quoted strings disabled by ANSI_QUOTES)
[3] MySQL Manual: PREPARE statement (statement is prepared once; parameter types determined at first prepare and retained)


🏁 Script executed:

# Check if the file exists and view the relevant lines
if [ -f "src/main/resources/db/migration/dbiemr/V9__1097Subcategorychanges.sql" ]; then
  echo "=== File found, displaying context around line 21 ==="
  sed -n '15,25p' "src/main/resources/db/migration/dbiemr/V9__1097Subcategorychanges.sql" | cat -n
  echo ""
  echo "=== File found, displaying context around line 40 ==="
  sed -n '35,45p' "src/main/resources/db/migration/dbiemr/V9__1097Subcategorychanges.sql" | cat -n
else
  echo "File not found at expected path"
  # Try to find it
  find . -name "V9__1097Subcategorychanges.sql" 2>/dev/null
fi

Repository: PSMRI/AMRIT-DB

Length of output: 1703


Update fallback strings to use escaped single quotes for ANSI_QUOTES compatibility.

The fallback path 'SELECT "Column SubCategoryID already exists";' becomes unsafe if the MySQL server runs with ANSI_QUOTES in its sql_mode. Under ANSI_QUOTES, double quotes are interpreted as identifier delimiters (not string delimiters), causing MySQL to parse "Column SubCategoryID already exists" as a column reference, which fails with an Unknown column error when executed via PREPARE/EXECUTE.

Single-quoted strings remain safe regardless of sql_mode settings. Apply the same fix to both fallback strings at lines 21 and 40:

Proposed fixes
 SET `@sql` := IF(
     `@col_exists` = 0,
     'ALTER TABLE t_kmfilemanager ADD COLUMN SubCategoryID INT NULL;',
-    'SELECT "Column SubCategoryID already exists";'
+    'SELECT ''Column SubCategoryID already exists'';'
 );
 SET `@sql2` := IF(
     `@fk_exists` = 0,
     'ALTER TABLE t_kmfilemanager 
         ADD CONSTRAINT FK_KmFileManager_SubCategory
         FOREIGN KEY (SubCategoryID)
         REFERENCES m_subcategory(SubCategoryID);',
-    'SELECT "Foreign key FK_KmFileManager_SubCategory already exists";'
+    'SELECT ''Foreign key FK_KmFileManager_SubCategory already exists'';'
 );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'SELECT "Column SubCategoryID already exists";'
'SELECT ''Column SubCategoryID already exists'';'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/db/migration/dbiemr/V9__1097Subcategorychanges.sql` at
line 21, Replace the fallback SQL literals that use double quotes with
single-quoted strings escaped for embedding: find the occurrences of the literal
'SELECT "Column SubCategoryID already exists";' (and the similar fallback at the
other occurrence) in V9__1097Subcategorychanges.sql and change them to use
escaped single quotes for the inner string (i.e., embed single quotes by
doubling them) so the PREPARE/EXECUTE path becomes safe under ANSI_QUOTES;
update both occurrences accordingly.

paras-dba and others added 27 commits February 18, 2026 21:37
* fix:changes in face embedding sql file version 2

* fix:changes sql file for migratio

* fix:changes for all migrationsql

* fix:new final fixes for deployement

* fix:changes on 28thNov

* fix:new changes 30/11/2025
* fix:changes in face embedding sql file version 2

* fix:changes sql file for migratio

* fix:changes for all migrationsql

* fix:new final fixes for deployement

* fix:changes on 28thNov

* fix:new changes 30/11/2025

* fix:new changes part-2 30/11/2025

* fix:new changes  02/12/2025

* fix:new changes part-2  02/12/2025

* fix:new changes part-3  02/12/2025

---------

Co-authored-by: vishwab1 <vishwanath@navadhiti.com>
* fix:changes in face embedding sql file version 2

* fix:changes sql file for migratio

* fix:changes for all migrationsql

* fix:new final fixes for deployement

* fix:changes on 28thNov

* fix:new changes 30/11/2025

* fix:new changes part-2 30/11/2025

* fix:new changes  02/12/2025

* fix:new changes part-2  02/12/2025

* fix:new changes part-3  02/12/2025

* fix: new fixes for sanofi prod 5/12/2025

---------

Co-authored-by: vishwab1 <vishwanath@navadhiti.com>
…ution time (PSMRI#37)

* fix: optimize the query for yearly report

* fix: rename the version to resolve the conflicts

* fix: add the drop procedure

* fix: version number

* fix: coderabbit comments

* fix: version change
* fix: update the query to fix the issue in datasync

* fix: update the query to fix the issue in datasync

* fix: update the version number

* fix: remove unwanted file

* fix: add syncfacilityID in ItemStockExit table

* fix: remove the trim query

* fix: version number

* fix: version change
Co-authored-by: Mithun James <1007084+drtechie@users.noreply.github.com>
* fix:changes in face embedding sql file version 2

* fix:changes sql file for migratio

* fix:changes for all migrationsql

* fix:new final fixes for deployement

* fix:changes on 28thNov

* fix:new changes 30/11/2025

* fix:new changes part-2 30/11/2025

* fix:new changes  02/12/2025

* fix:new changes part-2  02/12/2025

* fix:new changes part-3  02/12/2025

* fix: new fixes for sanofi prod 5/12/2025

* fix:t_idrdetails column modification

* fix:Modify table's column and added SP

* fix:change in v_23

* fix:v28 file

* fix:12/12/2025

* fix:query fixed acc. to coderabbit

---------

Co-authored-by: vishwab1 <vishwanath@navadhiti.com>
* fix: mda form and leprosy form added

* fix: not exist

* arrange the level table

* fix:semicolon added
* fix:changes in face embedding sql file version 2

* fix:changes sql file for migratio

* fix:changes for all migrationsql

* fix:new final fixes for deployement

* fix:changes on 28thNov

* fix:new changes 30/11/2025

* fix:new changes part-2 30/11/2025

* fix:new changes  02/12/2025

* fix:new changes part-2  02/12/2025

* fix:new changes part-3  02/12/2025

* fix: new fixes for sanofi prod 5/12/2025

* fix:t_idrdetails column modification

* fix:Modify table's column and added SP

* fix:change in v_23

* fix:v28 file

* fix:12/12/2025

* fix:query fixed acc. to coderabbit

* fix:optimised stored procedure

---------

Co-authored-by: vishwab1 <vishwanath@navadhiti.com>
paras-dba and others added 28 commits February 18, 2026 21:40
* add new column

* fix coderabit comments

* change column name

* change column name
* AMM-2104,AMM-2105,AMM-2114

* fix coderabit comments

* fix db campaign_filariasis_mda

* fix sql

* fix sql
* migration for release 3.7.0

* migration for release 3.7.0

* migration for release 3.7.0
* add new column in hbn table

* fix
* add new column in hbn table

* add migration for children column

* add migration for children column
Co-authored-by: Mithun James <1007084+drtechie@users.noreply.github.com>
)

* fix: add the index for improving search response

* fix: update the file for migration
* add auto_increment on id feild

* add auto_increment on id feild

* add auto_increment on id feild

* add auto_increment on id feild

* remove duplicate db_iemr
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
12.8% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/main/resources/db/migration/dbiemr/V9__1097Subcategorychanges.sql (1)

1-10: ⚠️ Potential issue | 🟡 Minor

Remove the redundant USE db_iemr; statement.

Line 1 already contains use db_iemr;. The newly added USE db_iemr; at line 10 is a duplicate and should be removed.

🔧 Proposed fix
 use db_iemr;
 
 --  ALTER TABLE t_kmfilemanager ADD COLUMN SubCategoryID INT NULL;
 
 -- ALTER TABLE t_kmfilemanager ADD CONSTRAINT FK_KmFileManager_SubCategory
 --   FOREIGN KEY (SubCategoryID) REFERENCES m_subcategory(SubCategoryID);
 
 
 
-USE db_iemr;
-
 
 -- 1) ADD COLUMN IF NOT EXISTS
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/db/migration/dbiemr/V9__1097Subcategorychanges.sql` around
lines 1 - 10, Remove the duplicate database selection: in
V9__1097Subcategorychanges.sql there are two occurrences of the statement "USE
db_iemr;"; delete the second one (the later "USE db_iemr;") so only the initial
use db_iemr; remains and no redundant database switch is present.
src/main/resources/db/migration/dbiemr/V13__QUALITY_MODULE_CATEGORY_MAPPING.sql (3)

33-37: ⚠️ Potential issue | 🟠 Major

Hardcoded DEFINER will cause ERROR 1449 on any environment where piramaldev doesn't exist.

DEFINER = \piramaldev`@`%`is baked into the migration. TheCREATE VIEWstatement will succeed at migration time, but any subsequent query against the view will throwERROR 1449: The user specified as a definer does not existon environments (staging, prod, CI) where this specific MySQL user is absent. The stored procedure on line 67 correctly omits aDEFINER` clause — the same approach should be applied to the view.

🐛 Proposed fix — drop the `DEFINER` clause so MySQL defaults to the current user
 CREATE 
     ALGORITHM = UNDEFINED 
-    DEFINER = `piramaldev`@`%` 
     SQL SECURITY DEFINER
 VIEW `db_iemr`.`v_get_qualityaudit_sectionquestionairevalues` AS
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/resources/db/migration/dbiemr/V13__QUALITY_MODULE_CATEGORY_MAPPING.sql`
around lines 33 - 37, The CREATE VIEW statement for
v_get_qualityaudit_sectionquestionairevalues includes a hardcoded
DEFINER=`piramaldev`@`%` which will cause ERROR 1449 in environments where that
user doesn't exist; remove the entire DEFINER = `piramaldev`@`%` (and any
ALGORITHM/SQL SECURITY clauses if you prefer leaving only the portable CREATE
VIEW ... AS ...) so the view is created without an explicit definer and will
default to the current user, mirroring the stored procedure approach used later
in this migration.

67-91: ⚠️ Potential issue | 🟠 Major

Missing soft-delete filter — procedure may return deleted records.

The view counterpart (lines 56–59) explicitly guards all three joined tables with Deleted IS FALSE. The stored procedure returns q.deleted in its SELECT list (line 79) but never filters on s.Deleted or q.Deleted in the WHERE clause. Callers will receive logically deleted sections and questions, which is inconsistent with the view's contract.

🐛 Proposed fix — add deleted guards matching the view's behavior
     WHERE s.providerservicemapid = IFNULL(v_psmrid, s.providerservicemapid)
       AND s.providerservicemapid IS NOT NULL
-      AND s.id IS NOT NULL;
+      AND s.Deleted IS FALSE
+      AND q.Deleted IS FALSE;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/resources/db/migration/dbiemr/V13__QUALITY_MODULE_CATEGORY_MAPPING.sql`
around lines 67 - 91, The stored procedure Pr_QualityAuditorSectionQuestionaire
returns rows from m_qualityauditsection (alias s) joined with
m_qualityauditquestionnaire (alias q) but does not filter out soft-deleted rows;
update the WHERE clause to mirror the view by adding guards for s.Deleted =
FALSE and q.Deleted = FALSE (or IS FALSE) in addition to the existing
providerservicemapid/NULL checks so logically deleted sections and questions are
excluded.

15-19: ⚠️ Potential issue | 🟡 Minor

Double-quoted string literal breaks under ANSI_QUOTES SQL mode.

"Column already exists" on line 18 uses double quotes, which are treated as identifier delimiters (not string literals) when ANSI_QUOTES is enabled. This would cause the no-op fallback branch to fail with a syntax error.

🐛 Proposed fix
 SET `@sql` = IF(
     `@col_exists` = 0,
     'ALTER TABLE m_qualityauditquestionnaire ADD COLUMN Role VARCHAR(50) DEFAULT ''Associate,ANM,MO'';',
-    'SELECT "Column already exists";'
+    'SELECT ''Column already exists'';'
 );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/resources/db/migration/dbiemr/V13__QUALITY_MODULE_CATEGORY_MAPPING.sql`
around lines 15 - 19, The fallback branch of the IF assigns a double-quoted
token ("Column already exists") which is an identifier under ANSI_QUOTES; update
the no-op branch in the `@sql` assignment so it uses a proper string literal
(single quotes) or an ANSI-safe expression (e.g., SELECT 'Column already
exists') instead; modify the IF that sets `@sql` (referencing `@col_exists` and `@sql`
in V13__QUALITY_MODULE_CATEGORY_MAPPING.sql) to replace the double-quoted value
with a single-quoted string.
src/main/resources/db/migration/dbiemr/V22__DB_IMER_incentive.sql (1)

40-44: ⚠️ Potential issue | 🟡 Minor

Typo in column names: "assame" should be "assamese".

Lines 40 and 43 contain assame_activity_description and assame_group_name, which should be assamese_activity_description and assamese_group_name (referring to the Assamese language). No application code currently references these column names, making this a safe change to apply now before deployment.

♻️ Suggested fix
-   `assame_activity_description` VARCHAR(1000) DEFAULT NULL,
+   `assamese_activity_description` VARCHAR(1000) DEFAULT NULL,
    `hindi_activity_description` VARCHAR(1000) DEFAULT NULL,
    `activity_id` BIGINT DEFAULT NULL,
-   `assame_group_name` VARCHAR(200) DEFAULT NULL,
+   `assamese_group_name` VARCHAR(200) DEFAULT NULL,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/db/migration/dbiemr/V22__DB_IMER_incentive.sql` around
lines 40 - 44, The two column names `assame_activity_description` and
`assame_group_name` are misspelled and should be renamed to
`assamese_activity_description` and `assamese_group_name`; update the migration
SQL to replace those identifiers (preserving types VARCHAR(1000)/VARCHAR(200)
and DEFAULT NULL) and also check and update any related constraints, indexes, or
references in the same migration (e.g., ALTER TABLE definitions or column lists)
to use the corrected `assamese_*` names so the schema is consistent.

Comment on lines +8 to +12
declare v_NextAttemptPeriod int(11);
select distinct NextAttemptPeriod into v_NextAttemptPeriod from m_mctscallconfiguration
where current_date() between effectivefrom and EffectiveUpto
and OutboundCallType like '%ecd%'
and deleted is false;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

v_NextAttemptPeriod will be NULL if no configuration row matches, causing silent empty results.

If the SELECT ... INTO finds no rows, v_NextAttemptPeriod remains NULL. All subsequent comparisons (v_NextAttemptPeriod=0, v_NextAttemptPeriod>0) evaluate to NULL, so the CASE in the WHERE clause (Lines 137–149) returns NULL for every row, and the procedure silently returns zero results with no error.

Add a guard after the SELECT INTO to handle the NULL case — either default to a sensible value or raise a signal:

Proposed fix
 declare v_NextAttemptPeriod int(11);
 select distinct NextAttemptPeriod into v_NextAttemptPeriod from m_mctscallconfiguration
  where current_date() between effectivefrom and EffectiveUpto 
 and OutboundCallType like '%ecd%' 
  and deleted is false;
+
+IF v_NextAttemptPeriod IS NULL THEN
+  SET v_NextAttemptPeriod = 0;
+END IF;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
declare v_NextAttemptPeriod int(11);
select distinct NextAttemptPeriod into v_NextAttemptPeriod from m_mctscallconfiguration
where current_date() between effectivefrom and EffectiveUpto
and OutboundCallType like '%ecd%'
and deleted is false;
declare v_NextAttemptPeriod int(11);
select distinct NextAttemptPeriod into v_NextAttemptPeriod from m_mctscallconfiguration
where current_date() between effectivefrom and EffectiveUpto
and OutboundCallType like '%ecd%'
and deleted is false;
IF v_NextAttemptPeriod IS NULL THEN
SET v_NextAttemptPeriod = 0;
END IF;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/db/migration/dbiemr/V34__SP_ECD.sql` around lines 8 - 12,
v_NextAttemptPeriod can be NULL when the SELECT ... INTO from
m_mctscallconfiguration returns no rows, which makes subsequent comparisons in
the CASE (used in the WHERE clause around the v_NextAttemptPeriod=0 / >0 logic)
evaluate to NULL and produce empty results; after the SELECT INTO that populates
v_NextAttemptPeriod add a guard: check IF v_NextAttemptPeriod IS NULL THEN
either SET v_NextAttemptPeriod to a sensible default (e.g., 0 or another
business-appropriate value) or RAISE/ SIGNAL an error with a clear message so
the procedure does not silently return zero rows — update the code surrounding
v_NextAttemptPeriod and the CASE used in the WHERE to rely on this non-NULL
value.

Comment on lines +9 to +12
declare v_NextAttemptPeriod int(11);
select distinct NextAttemptPeriod into v_NextAttemptPeriod from m_mctscallconfiguration where current_date() between effectivefrom and EffectiveUpto
and OutboundCallType like '%ecd%'
and deleted is false;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Same NULL v_NextAttemptPeriod risk as V34 — apply the same guard here.

If no matching configuration row is found, v_NextAttemptPeriod will be NULL, and the CASE filter on Lines 128–136 will silently exclude all rows. Add the same IF v_NextAttemptPeriod IS NULL THEN SET v_NextAttemptPeriod = 0; END IF; guard recommended for V34.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/db/migration/dbiemr/V35__ECD_Child_SP.sql` around lines 9
- 12, The variable v_NextAttemptPeriod populated from m_mctscallconfiguration
can be NULL if no rows match; after the SELECT into v_NextAttemptPeriod add a
guard: check IF v_NextAttemptPeriod IS NULL THEN set v_NextAttemptPeriod = 0;
END IF; so downstream CASE filtering that uses v_NextAttemptPeriod (in this
procedure) does not silently exclude all rows.

Comment on lines +115 to +121
left join (
-- select obcallid,max(createddate) max_createddate from t_bencall group by obcallid
select t1.obcallid,max(t1.createddate) max_createddate from t_bencall t1
inner join t_MCTSOutboundCalls t2 on t1.obcallid=t2.obcallid where t1.CalledServiceID=1714 and AllocatedUserID = v_AllocatedUserID
group by t1.obcallid
) b2 on b2.obcallid=mctsoutbou0_.obcallid
left join t_bencall b on b.obcallid=b2.obcallid and b.createddate=b2.max_createddate
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Missing callendtime IS NOT NULL filter in subqueries — inconsistent with the mother procedure (V34).

In V34 (mother procedure), both the b2 subquery and the b join include filters ensuring only completed calls are considered:

  • b2: t1.callendtime is not null (V34 Line 127)
  • b join: b.CalledServiceID=1714 and b.callendtime is not null (V34 Line 130)

In V35, both are missing:

  • Line 118: no t1.callendtime is not null
  • Line 121: no b.CalledServiceID=1714 and b.callendtime is not null

This means the child procedure may match in-progress (incomplete) call records when computing isCallDisconnected and sort_createddate, leading to incorrect worklist ordering or stale data.

Proposed fix to align with V34
 left join (
  select t1.obcallid,max(t1.createddate) max_createddate from t_bencall t1
- inner join t_MCTSOutboundCalls t2 on t1.obcallid=t2.obcallid where t1.CalledServiceID=1714 and AllocatedUserID = v_AllocatedUserID
+ inner join t_MCTSOutboundCalls t2 on t1.obcallid=t2.obcallid where t1.CalledServiceID=1714 and t2.AllocatedUserID = v_AllocatedUserID and t1.callendtime is not null
  group by t1.obcallid
  ) b2 on b2.obcallid=mctsoutbou0_.obcallid
- left join t_bencall b on b.obcallid=b2.obcallid and b.createddate=b2.max_createddate
+ left join t_bencall b on b.obcallid=b2.obcallid and b.createddate=b2.max_createddate and b.CalledServiceID=1714 and b.callendtime is not null
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
left join (
-- select obcallid,max(createddate) max_createddate from t_bencall group by obcallid
select t1.obcallid,max(t1.createddate) max_createddate from t_bencall t1
inner join t_MCTSOutboundCalls t2 on t1.obcallid=t2.obcallid where t1.CalledServiceID=1714 and AllocatedUserID = v_AllocatedUserID
group by t1.obcallid
) b2 on b2.obcallid=mctsoutbou0_.obcallid
left join t_bencall b on b.obcallid=b2.obcallid and b.createddate=b2.max_createddate
left join (
-- select obcallid,max(createddate) max_createddate from t_bencall group by obcallid
select t1.obcallid,max(t1.createddate) max_createddate from t_bencall t1
inner join t_MCTSOutboundCalls t2 on t1.obcallid=t2.obcallid where t1.CalledServiceID=1714 and t2.AllocatedUserID = v_AllocatedUserID and t1.callendtime is not null
group by t1.obcallid
) b2 on b2.obcallid=mctsoutbou0_.obcallid
left join t_bencall b on b.obcallid=b2.obcallid and b.createddate=b2.max_createddate and b.CalledServiceID=1714 and b.callendtime is not null
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/db/migration/dbiemr/V35__ECD_Child_SP.sql` around lines
115 - 121, The b2 subquery and the subsequent join to alias b in
V35__ECD_Child_SP.sql are missing the "completed call" filter, causing
in-progress calls to be included; update the b2 subquery (alias t1 in t_bencall)
to include "t1.callendtime IS NOT NULL" in its WHERE clause (keeping the
existing CalledServiceID=1714 and AllocatedUserID = v_AllocatedUserID) and add
the same completion filter to the join condition for b (i.e., include
"b.CalledServiceID=1714 AND b.callendtime IS NOT NULL" when joining b on
b2.obcallid) so behavior matches V34's b2 and b filters and prevents incomplete
calls from affecting isCallDisconnected and sort_createddate.

Comment on lines +3 to +90
SET @table_exists = (
SELECT COUNT(*)
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'db_iemr'
AND TABLE_NAME = 't_cdr'
);

-- Proceed only if table exists
SET @sql_check = IF(
@table_exists > 0,
'SELECT "Table t_cdr exists. Proceeding with migration..." AS status',
'SELECT "ERROR: Table t_cdr does not exist!" AS status'
);
PREPARE stmt FROM @sql_check;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

-- Add cdr_image_1 (LONGTEXT)
SET @column_exists_1 = (
SELECT COUNT(*)
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'db_iemr'
AND TABLE_NAME = 't_cdr'
AND COLUMN_NAME = 'cdr_image_1'
);

SET @sql_add_column_1 = IF(
@column_exists_1 = 0,
'ALTER TABLE db_iemr.t_cdr ADD COLUMN cdr_image_1 LONGTEXT DEFAULT NULL COMMENT "First CDR image (base64 encoded or file path)"',
'SELECT "Column cdr_image_1 already exists, skipping..." AS status'
);
PREPARE stmt FROM @sql_add_column_1;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

-- Add cdr_image_2 (LONGTEXT)
SET @column_exists_2 = (
SELECT COUNT(*)
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'db_iemr'
AND TABLE_NAME = 't_cdr'
AND COLUMN_NAME = 'cdr_image_2'
);

SET @sql_add_column_2 = IF(
@column_exists_2 = 0,
'ALTER TABLE db_iemr.t_cdr ADD COLUMN cdr_image_2 LONGTEXT DEFAULT NULL COMMENT "Second CDR image (base64 encoded or file path)"',
'SELECT "Column cdr_image_2 already exists, skipping..." AS status'
);
PREPARE stmt FROM @sql_add_column_2;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

-- Add death_cert_image_1 (LONGTEXT)
SET @column_exists_3 = (
SELECT COUNT(*)
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'db_iemr'
AND TABLE_NAME = 't_cdr'
AND COLUMN_NAME = 'death_cert_image_1'
);

SET @sql_add_column_3 = IF(
@column_exists_3 = 0,
'ALTER TABLE db_iemr.t_cdr ADD COLUMN death_cert_image_1 LONGTEXT DEFAULT NULL COMMENT "First death certificate image (base64 encoded or file path)"',
'SELECT "Column death_cert_image_1 already exists, skipping..." AS status'
);
PREPARE stmt FROM @sql_add_column_3;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

-- Add death_cert_image_2 (LONGTEXT)
SET @column_exists_4 = (
SELECT COUNT(*)
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'db_iemr'
AND TABLE_NAME = 't_cdr'
AND COLUMN_NAME = 'death_cert_image_2'
);

SET @sql_add_column_4 = IF(
@column_exists_4 = 0,
'ALTER TABLE db_iemr.t_cdr ADD COLUMN death_cert_image_2 LONGTEXT DEFAULT NULL COMMENT "Second death certificate image (base64 encoded or file path)"',
'SELECT "Column death_cert_image_2 already exists, skipping..." AS status'
);
PREPARE stmt FROM @sql_add_column_4;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

t_cdr block has no @proceed guard — migration will crash if the table is absent

The @table_exists check for t_cdr only emits a status message; it never sets a @proceed flag. If t_cdr does not exist:

  1. @column_exists_N evaluates to 0 (no rows in INFORMATION_SCHEMA.COLUMNS for a missing table).
  2. Each IF(@column_exists_N = 0, 'ALTER TABLE ...', ...) resolves to the ALTER TABLE string.
  3. EXECUTE stmt fires an ALTER TABLE against a non-existent table → MySQL Error 1146 → Flyway migration fails.

The t_mdsr block (line 110) correctly guards against this with SET @proceed = IF(@table_exists > 0, 1, 0). Apply the same pattern to t_cdr.

🐛 Proposed fix — add `@proceed` guard to the `t_cdr` block
  PREPARE stmt FROM `@sql_check`;
  EXECUTE stmt;
  DEALLOCATE PREPARE stmt;

+ -- Stop if table doesn't exist
+ SET `@proceed` = IF(`@table_exists` > 0, 1, 0);
+
  -- Add cdr_image_1 (LONGTEXT)
  SET `@column_exists_1` = (
      SELECT COUNT(*)
      FROM information_schema.COLUMNS
      WHERE TABLE_SCHEMA = 'db_iemr'
      AND TABLE_NAME = 't_cdr'
      AND COLUMN_NAME = 'cdr_image_1'
  );

  SET `@sql_add_column_1` = IF(
-     `@column_exists_1` = 0,
-     'ALTER TABLE db_iemr.t_cdr ADD COLUMN cdr_image_1 LONGTEXT DEFAULT NULL COMMENT "First CDR image (base64 encoded or file path)"',
-     'SELECT "Column cdr_image_1 already exists, skipping..." AS status'
+     `@column_exists_1` = 0 AND `@proceed` = 1,
+     'ALTER TABLE db_iemr.t_cdr ADD COLUMN cdr_image_1 LONGTEXT DEFAULT NULL COMMENT \'First CDR image (base64 encoded or file path)\'',
+     IF(`@column_exists_1` > 0,
+         'SELECT \'Column cdr_image_1 already exists, skipping...\' AS status',
+         'SELECT \'Skipping due to table not found\' AS status'
+     )
  );

Apply the same pattern to cdr_image_2, death_cert_image_1, and death_cert_image_2.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/db/migration/dbiemr/V45__cdr_mdr_dbmigtaion.sql` around
lines 3 - 90, Add a `@proceed` guard like the t_mdsr block: after computing
`@table_exists` for t_cdr, set SET `@proceed` = IF(`@table_exists` > 0, 1, 0) and then
change each column-add conditional to require `@proceed` = 1 as well (e.g. build
`@sql_add_column_1`..4 with IF(`@proceed` = 1 AND `@column_exists_N` = 0, 'ALTER TABLE
db_iemr.t_cdr ADD COLUMN ...', 'SELECT "Column ... already exists or table
missing, skipping..." AS status')) so PREPARE/EXECUTE stmt never runs an ALTER
TABLE when the table is absent; update all referenced variables
(`@column_exists_1`..4, `@sql_add_column_1`..4, PREPARE stmt, EXECUTE stmt)
accordingly.

SET @preparedStatement = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = @columnname) > 0,
'SELECT 1',
CONCAT('ALTER TABLE ', @dbname, '.', @tablename, ' ADD COLUMN ', @columnname, ' VARCHAR(255) DEFAULT NULL;')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n src/main/resources/db/migration/dbiemr/V56__t_cbacdetails_migration.sql

Repository: PSMRI/AMRIT-DB

Length of output: 4292


🌐 Web query:

MySQL PREPARE statement trailing semicolon documented behavior

💡 Result:

In MySQL, the semicolon (;) is a client-side statement delimiter, not part of the SQL statement text sent to the server. The mysql CLI (and similar tools) treat ; as “end of statement,” and this behavior is controlled by the client’s delimiter setting (default ;).[2]

PREPARE stmt_name FROM preparable_stmt is different: the SQL to prepare is inside a string literal / user variable and is parsed by the server as statement text. The MySQL manual specifies that preparable_stmt “must represent a single statement, not multiple statements.”[1] A trailing semicolon inside the prepared SQL string therefore does not act as a delimiter; it’s an extra token in the statement text and commonly results in a syntax error (for example, PREPARE s FROM 'SELECT 1;'; fails, while PREPARE s FROM 'SELECT 1'; works), because the server expects the statement to end after the single statement.[1]

Sources
[1] MySQL Reference Manual – PREPARE statement (“single statement, not multiple statements”) https://dev.mysql.com/doc/refman/8.4/en/prepare.html
[2] MySQL Reference Manual – mysql client delimiter command (client interprets statement separator; default ;) https://dev.mysql.com/doc/refman/5.7/en/mysql-commands.html


Remove trailing semicolons from prepared statement strings — this will cause the migration to fail

Each CONCAT call on lines 11, 21, 31, 41, 51, 61, 71, and 81 embeds a trailing ; in the generated SQL string. According to the MySQL manual, a prepared statement string preparable_stmt "must represent a single statement, not multiple statements." A trailing semicolon inside the string is parsed as an extra token by the server, causing a syntax error when the prepared statement executes. For example, PREPARE stmt FROM 'SELECT 1;'; fails, while PREPARE stmt FROM 'SELECT 1'; succeeds. This inconsistency with the 'SELECT 1' no-op branch (which correctly omits any terminator) confirms the error.

🔴 Proposed fix — remove the trailing semicolons from all CONCAT statements
-  CONCAT('ALTER TABLE ', `@dbname`, '.', `@tablename`, ' ADD COLUMN ', `@columnname`, ' VARCHAR(255) DEFAULT NULL;')
+  CONCAT('ALTER TABLE ', `@dbname`, '.', `@tablename`, ' ADD COLUMN ', `@columnname`, ' VARCHAR(255) DEFAULT NULL')

Apply the same change to all eight CONCAT calls (lines 11, 21, 31, 41, 51, 61, 71, 81), including the TINYINT(1) variant on line 81.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
CONCAT('ALTER TABLE ', @dbname, '.', @tablename, ' ADD COLUMN ', @columnname, ' VARCHAR(255) DEFAULT NULL;')
CONCAT('ALTER TABLE ', `@dbname`, '.', `@tablename`, ' ADD COLUMN ', `@columnname`, ' VARCHAR(255) DEFAULT NULL')
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/db/migration/dbiemr/V56__t_cbacdetails_migration.sql` at
line 11, The CONCAT expressions used to build prepared statements (e.g.,
CONCAT('ALTER TABLE ', `@dbname`, '.', `@tablename`, ' ADD COLUMN ', `@columnname`, '
VARCHAR(255) DEFAULT NULL;')) include a trailing semicolon which makes PREPARE
stmt fail; remove the trailing ';' from all CONCAT calls (including the
VARCHAR(255) and TINYINT(1) variants) so the generated preparable_stmt contains
no terminator, ensuring PREPARE/EXECUTE work correctly.

Comment on lines +61 to +69
SET @columnname = 'steilisation_date';
SET @preparedStatement = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = @columnname) > 0,
'SELECT 1',
CONCAT('ALTER TABLE ', @dbname, '.', @tablename, ' ADD COLUMN ', @columnname, ' TIMESTAMP NULL;')
));
PREPARE stmt FROM @preparedStatement;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Search for any Java references to sterilisation/steilisation variants
rg -i 'steil[ia]sation|steil[ia]sation_date' --type java -A 2 -B 2

Repository: PSMRI/AMRIT-DB

Length of output: 40


🏁 Script executed:

# Search for sterilisation/sterilization variants in Java
rg -i 'sterili[sz]ation|sterili[sz]ation_date' --type java -A 2 -B 2

Repository: PSMRI/AMRIT-DB

Length of output: 40


🏁 Script executed:

# Check migration files for existing column definitions
rg -i 'steril' . --include='*.sql' -A 1 -B 1

Repository: PSMRI/AMRIT-DB

Length of output: 499


🏁 Script executed:

# Look for PNCVisit or similar entity definitions
fd -e java -type f | xargs grep -l 'PNCVisit\|PnC\|pnc_visit' | head -20

Repository: PSMRI/AMRIT-DB

Length of output: 228


🏁 Script executed:

# First, check if this is a database-only repository
ls -la | head -20

Repository: PSMRI/AMRIT-DB

Length of output: 879


🏁 Script executed:

# Search SQL files for sterilisation/steilisation variants
rg -i 'steril' -t sql

Repository: PSMRI/AMRIT-DB

Length of output: 383


🏁 Script executed:

# Look at the context around the V58 migration file
cat -n src/main/resources/db/migration/dbiemr/V58__missing_migration.sql

Repository: PSMRI/AMRIT-DB

Length of output: 6252


🏁 Script executed:

# Check previous migrations for any sterilisation column references
rg -i 'steril' src/main/resources/db/migration/dbiemr/ -t sql -B 1 -A 1

Repository: PSMRI/AMRIT-DB

Length of output: 770


steilisation_date is a confirmed typo — fix before this migration runs.

steilisation is a transposition error (ei ↔ ie). The correct spelling is sterilisation_date, which is consistent with the date_of_sterilisation column added in V48 for the ANC visit table. This permanent schema defect will break any application entity mapping that expects the correct spelling.

🐛 Proposed fix
-  SET `@columnname` = 'steilisation_date';
+  SET `@columnname` = 'sterilisation_date';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
SET @columnname = 'steilisation_date';
SET @preparedStatement = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = @columnname) > 0,
'SELECT 1',
CONCAT('ALTER TABLE ', @dbname, '.', @tablename, ' ADD COLUMN ', @columnname, ' TIMESTAMP NULL;')
));
PREPARE stmt FROM @preparedStatement;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET `@columnname` = 'sterilisation_date';
SET `@preparedStatement` = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = `@dbname` AND TABLE_NAME = `@tablename` AND COLUMN_NAME = `@columnname`) > 0,
'SELECT 1',
CONCAT('ALTER TABLE ', `@dbname`, '.', `@tablename`, ' ADD COLUMN ', `@columnname`, ' TIMESTAMP NULL;')
));
PREPARE stmt FROM `@preparedStatement`;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/db/migration/dbiemr/V58__missing_migration.sql` around
lines 61 - 69, The migration uses the misspelled column name 'steilisation_date'
via the variable `@columnname` and prepared statement (preparedStatement/stmt);
update the migration to use the correct column name 'sterilisation_date'
everywhere (set `@columnname`, any CONCAT for ALTER TABLE, and any existence
checks) so the migration adds/checks sterilisation_date (matching V48's
date_of_sterilisation naming), and run a quick search to ensure no downstream
code or other migrations reference the old misspelling before committing.

Comment on lines +20 to +25
SET @columnname = 'date';
SET @preparedStatement = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = @columnname) > 0,
'SELECT 1',
CONCAT('ALTER TABLE ', @dbname, '.', @tablename, ' ADD COLUMN ', @columnname, ' DATE DEFAULT NULL;')
));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

MySQL 8.0 DATE reserved keyword backtick quoting column name syntax error

💡 Result:

In MySQL 8.0, DATE / date can conflict with SQL parsing (it’s a keyword), so if you used it as a column name you must treat it as an identifier and quote it everywhere with backticks (not single quotes) [1].

Examples:

CREATE TABLE t (
  `date` DATE NOT NULL
);

SELECT `date` FROM t;
INSERT INTO t (`date`) VALUES ('2026-02-18');
ALTER TABLE t MODIFY COLUMN `date` DATE NOT NULL;

Common gotcha: if your “syntax error” is around a DEFAULT, note that as of MySQL 8.0.13 default values may be expressions, but expression defaults must generally be wrapped in parentheses [2][3]:

CREATE TABLE t (
  `date` DATE NOT NULL DEFAULT (CURRENT_DATE)
);

Best practice is still to rename the column (e.g., created_date, event_date) to avoid having to quote it in every query [1].

Sources: [1] MySQL 8.0 manual: keywords/reserved words and quoting identifiers, [2] MySQL 8.0 manual: default value rules (notably changes as of 8.0.13), [3] MySQL 8.0 example using DEFAULT (CURRENT_DATE) for a DATE column.


🏁 Script executed:

cat -n src/main/resources/db/migration/dbiemr/V59__sammelan_record.sql

Repository: PSMRI/AMRIT-DB

Length of output: 1250


date is a reserved MySQL keyword — the generated ALTER TABLE will fail with a syntax error unless the identifier is quoted

DATE is a reserved keyword in MySQL 8.0. When used as a column name, it must be enclosed in backticks to distinguish it from the keyword itself. The CONCAT on line 24 currently builds:

ALTER TABLE db_iemr.uwin_session_record ADD COLUMN date DATE DEFAULT NULL;

The unquoted date identifier will cause MySQL to reject the statement with Error 1064: You have an error in your SQL syntax when the migration executes.

Fix 1 (preferred): rename the column to something unambiguous (e.g., session_date — consistent with the existing pattern in sammelan_record — or event_date).

Fix 2 (if the name must stay): backtick-quote the identifier in the CONCAT:

🛠️ Proposed fix (backtick quoting)
-  CONCAT('ALTER TABLE ', `@dbname`, '.', `@tablename`, ' ADD COLUMN ', `@columnname`, ' DATE DEFAULT NULL;')
+  CONCAT('ALTER TABLE `', `@dbname`, '`.`', `@tablename`, '` ADD COLUMN `', `@columnname`, '` DATE DEFAULT NULL;')
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/db/migration/dbiemr/V59__sammelan_record.sql` around lines
20 - 25, The migration builds an ALTER TABLE to add a column named 'date' which
is a reserved MySQL keyword; change the schema to use a non-reserved name (e.g.,
set `@columnname` = 'session_date' to match sammelan_record pattern) and update
any downstream references, or if the column name must remain 'date' modify the
CONCAT that builds `@preparedStatement` to wrap the identifier in backticks (e.g.,
use CONCAT('ALTER TABLE ', `@dbname`, '.', `@tablename`, ' ADD COLUMN `',
`@columnname`, '` DATE DEFAULT NULL;')) and also ensure `@dbname` and `@tablename` are
properly quoted if needed; update usage of `@columnname` and `@preparedStatement`
accordingly.

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.

8 participants