mysql
Technical notes on web development, DevOps, and AI integration.
58 articles
- 00:12devops
Keeping the Users Table Alive When db-push Swaps the Database
db-push replaced staging from a dev dump and accounts vanished. Two small dumps and honest guards keep them alive.
TL;DR: Staging users kept vanishing because db-push replaces the whole database with a dev dump that excludes the users table. The fix: dump staging users beforehand, then restore them after the import using --no-data, --complete-insert, and --no-create-info. Key lesson: dump and verify before any DROP, validate injected names, and lock down the hash-bearing file.
#mysql#mysqldump#ssh - 23:12backend
Permanent Delete Without Orphan Media Rows or Files
The database rows disappear with the record, but the files stay on the server. A permanent delete collects its references before any row is dropped.
TL;DR: Database cascades only delete rows, never files, leaving orphaned uploads on disk. The fix collects upload URLs before deletion, then counts references across all URL-bearing columns to decide what is truly unused, removing files best-effort afterward with strict path validation. Failures are logged, not fatal, and tests force the orphan scenario to verify cleanup.
#golang#mysql#api - 21:53database
Slugs Carrying Curly-Quote Bytes: Fixed via HEX, Not Regex
Seven public slugs carried curly-quote bytes from a bad import. The fix: a HEX-guarded idempotent SQL migration with a byte-identical down path.
TL;DR: A HEX spot-check found seven venue slugs carrying curly quotes and en dashes from a bad import, not charset truncation. The fix skips regex, using manual ASCII maps guarded byte-exact by HEX checks, so migrations stay idempotent and rollbacks restore identical bytes. Same commit also blocks staging user-data overwrites and misdirected E2E runs.
#mysql#sql#data-migration - 21:06backend
When the ALL Sentinel Emptied the Admin List
One ALL string from the middleware was treated as a column value, and the announcement list lost every scoped record.
TL;DR: A super admin's announcement list returned zero rows because the ALL sentinel from auth was passed straight into SQL as a literal division filter. Since global rows use NULL, only IS NULL catches them, so nothing matched. Fix: treat ALL and empty string as no-filter at the repository boundary, backed by a regression test.
#golang#mysql#sql - 18:58backend
Admin Page 500 and the Count Query That Lost Its Join
A shared WHERE filter referenced an alias the count query never joined, so every admin list request died with a 500. The fix was one line.
TL;DR: A superadmin's review moderation page crashed with a 500 due to an unknown column error, despite the list API working fine. The shared filter referenced alias c.module, but the count query hadn't joined the categories table, so MySQL couldn't resolve it. Adding the missing LEFT JOIN fixed the count and restored pagination, highlighting that shared filters need matching joins.
#mysql#go#sql - 16:24database
Soft Delete Done Right: Predicates, RowsAffected, and Restore
Soft delete is not just DELETE swapped for UPDATE. Predicates on every query, a RowsAffected alarm, and a mirrored restore guard keep it honest.
TL;DR: Switching DELETE to soft delete with an UPDATE on deleted_at left deleted rows visible because SELECTs weren't filtered. Fixing it means adding deleted_at IS NULL to queries, checking RowsAffected to return 404, and mirroring logic for restores. You also need to invalidate cache on every state change, otherwise complexity and leaks quickly outweigh benefits.
#go#mysql#sql - 15:07backend
Three Nullable Shapes in a Go Write Path
Threading nullable columns through a Go CRUD: the PUT idiom, plain bools, a pointer ID, and an explicit null contract.
TL;DR: KotaPortal’s upsert adds four fields via three patterns: contact person uses empty-string-means-null, two booleans are always required, and type ID uses a nil pointer for null. PUT does a full overwrite, while responses avoid omitempty to ensure nulls appear explicitly for generated clients. New checks cap contact data at 100 characters and require type ID to be at least one.
#golang#api#mysql - 15:07backend
Designing a Down-First Migration: Additive Columns, Postponed FK
Designing a MySQL migration backwards: additive columns, a mirrored down file, and a foreign key deliberately postponed.
TL;DR: The migration adds nullable columns and indexes to entities and categories without imposing a foreign key yet. This additive approach keeps changes online and concurrent on InnoDB while ensuring rollback just drops what was added without rewriting data. Details like a nullable timestamp and deferred constraints prioritize safe rollbacks over premature relational perfection.
#mysql#golang#schema - 14:21backend
A Test That Passes by Day and Fails at Dawn
A date filter test failed every dawn from midnight to seven: two clocks in one test. Fix: derive dates from the database clock, pin midday.
TL;DR: A scheduled test failure came from two clocks: Go on a WIB host and MySQL in UTC, seven hours apart. Around midnight their dates diverged, so Go's yesterday still contained the database's today and assertions failed until morning. The fix uses MySQL's CURDATE as the single witness and pins rows to midday to stay clear of boundary drift.
#go#mysql#testing