{"record":{"id":"3a2b80b15f80bc71","repo":"Wei-Shaw/sub2api","slug":"concurrently-statements-must-be-placed-in-notx-s","errorCode":null,"errorMessage":"CONCURRENTLY statements must be placed in *_notx.sql migrations","messagePattern":"CONCURRENTLY statements must be placed in \\*_notx\\.sql migrations","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/internal/repository/migrations_runner.go","lineNumber":492,"sourceCode":"\tif !ok {\n\t\treturn false\n\t}\n\t_, dbOK := rule.acceptedChecksums[dbChecksum]\n\tif !dbOK {\n\t\treturn false\n\t}\n\t_, fileOK := rule.acceptedChecksums[fileChecksum]\n\treturn fileOK\n}\n\nfunc validateMigrationExecutionMode(name, content string) (bool, error) {\n\tnormalizedName := strings.ToLower(strings.TrimSpace(name))\n\tupperContent := strings.ToUpper(content)\n\tnonTx := strings.HasSuffix(normalizedName, nonTransactionalMigrationSuffix)\n\n\tif !nonTx {\n\t\tif strings.Contains(upperContent, \"CONCURRENTLY\") {\n\t\t\treturn false, errors.New(\"CONCURRENTLY statements must be placed in *_notx.sql migrations\")\n\t\t}\n\t\treturn false, nil\n\t}\n\n\tif strings.Contains(upperContent, \"BEGIN\") || strings.Contains(upperContent, \"COMMIT\") || strings.Contains(upperContent, \"ROLLBACK\") {\n\t\treturn false, errors.New(\"*_notx.sql must not contain transaction control statements (BEGIN/COMMIT/ROLLBACK)\")\n\t}\n\n\tstatements := splitSQLStatements(content)\n\tfor _, stmt := range statements {\n\t\tnormalizedStmt := strings.ToUpper(stripSQLLineComment(strings.TrimSpace(stmt)))\n\t\tif normalizedStmt == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tif strings.Contains(normalizedStmt, \"CONCURRENTLY\") {\n\t\t\tisCreateIndex := strings.Contains(normalizedStmt, \"CREATE\") && strings.Contains(normalizedStmt, \"INDEX\")\n\t\t\tisDropIndex := strings.Contains(normalizedStmt, \"DROP\") && strings.Contains(normalizedStmt, \"INDEX\")","sourceCodeStart":474,"sourceCodeEnd":510,"githubUrl":"https://github.com/Wei-Shaw/sub2api/blob/073e92d17178a1ccdb0a27017f572f10c9c7ab62/backend/internal/repository/migrations_runner.go#L474-L510","documentation":"The migration runner validates each .sql file's execution mode. Any migration NOT ending in the *_notx.sql suffix that contains the keyword CONCURRENTLY (case-insensitive substring on uppercased content) is rejected, because CREATE/DROP INDEX CONCURRENTLY cannot run inside a transaction and regular migrations are wrapped in one.","triggerScenarios":"Adding a migration like 0007_add_idx.sql containing 'CREATE INDEX CONCURRENTLY ...' — the runner detects CONCURRENTLY, sees the filename lacks the _notx suffix, and fails before executing anything.","commonSituations":"Developers copying index migrations from other projects that use CONCURRENTLY by default; adding REINDEX ... or COMMENT ... CONCURRENTLY-adjacent syntax; renaming a _notx file and dropping the suffix while keeping the content.","solutions":["Rename the file to end with the non-transactional suffix (e.g. 0007_add_idx_notx.sql) so the runner executes it outside a transaction.","If the index is small/table is new, drop CONCURRENTLY and keep it a normal transactional migration.","Re-run migrations after fixing; validation happens pre-execution so no partial state results."],"exampleFix":"-- file: migrations/0007_add_idx.sql  (rejected)\nCREATE INDEX CONCURRENTLY idx_events_account ON events(account_id);\n\n-- file: migrations/0007_add_idx_notx.sql  (accepted)\nCREATE INDEX CONCURRENTLY IF NOT EXISTS idx_events_account ON events(account_id);","handlingStrategy":"validation","validationCode":"// pre-commit / CI check for migration files\nfunc checkMigration(name, content string) error {\n    nonTx := strings.HasSuffix(strings.ToLower(name), \"_notx.sql\")\n    hasConc := strings.Contains(strings.ToUpper(content), \"CONCURRENTLY\")\n    if hasConc && !nonTx {\n        return fmt.Errorf(\"%s: rename to *_notx.sql or drop CONCURRENTLY\", name)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Add a CI lint for migration naming vs CONCURRENTLY usage","Template new index migrations as _notx from the start","Remember CONCURRENTLY cannot run inside the default transactional runner"],"tags":["database","migrations","postgres","validation"],"backgroundTag":null,"analyzedSha":"073e92d17178a1ccdb0a27017f572f10c9c7ab62","analyzedAt":"2026-08-15T14:33:00.750Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}