mysql
Technical notes on web development, DevOps, and AI integration.
58 articles
- 04:28backend
Seed Migration and Status Mapping for Government Facility Data Import
Creating new categories through idempotent seed migrations, then importing facility data with status-based publish mapping for existing and planned facilities.
TL;DR: A missing Sarana Budaya category blocked importing eight cultural facilities, so the team added it via an idempotent seed migration using ON DUPLICATE KEY UPDATE. Existing facilities import as published, planned ones as drafts with admin notes. The importer ships with dry-run mode, a development-only guard, and slug-based deduplication for safe, repeatable runs.
#go#mysql#data-import - 04:23backend
Corrective Cultural Heritage Data: Pool Matching Without a Unique Key
The corrective import of PPKD cultural heritage data failed because the SK number wasn't unique. Candidate pools and overlap coefficient solve 1:1 matching.
TL;DR: Importing PPKD heritage data broke when SK numbers turned out to be shared across multiple objects, so direct key matching corrupted good records. The fix pairs each row with the best name match from an SK's candidate pool using the overlap coefficient, plus a used guard preventing double assignment. Column ownership rules keep verified coordinates and media untouched.
#go#mysql#data-import - 23:55backend
Plain Date, Not Instant: the event_date Contract at KotaPortal
The admin event_date once left the browser as an instant T00:00:00Z. The contract now requires a plain YYYY-MM-DD string, with Go validation rejecting instants outright.
TL;DR: A calendar-date field was leaking through the API as a UTC midnight instant, silently accepted by lenient MariaDB and patched over client-side with toLocalDateInput. The fix changes the contract itself: format date, a string DTO, validation at the service edge with a 400 on bad input. Regenerated frontend types now reject instants at compile time, killing the timezone-drift bug class.
#api#openapi#go - 05:37backend
Out-of-System Payments: a Marker, Not a Module
An externally verified payment modeled as two nullable columns: required *bool contract, a dedicated 409, and a two-arm union SQL.
TL;DR: The system never handles payments, so the fix adds just two nullable columns, payment_marked and payment_marked_at, to track when treasury verifies fees externally. A guarded endpoint uses *bool decoding so missing fields fail loudly instead of silently clearing the marker. Public API hides the columns, admin API exposes them, and submission status stays untouched.
#go#mysql#api - 05:23backend
Schedule Conflict Checks as a Warning, Not a Wall
A booking form that warns about schedule clashes without blocking: read-only endpoint, inclusive overlap query, debounce and abort.
TL;DR: A hall got double-booked because two officers approved overlapping applications without checking each other, and the old form couldn't flag taken dates. The shipped fix pairs a validated, read-only endpoint that flags overlapping schedules with a debounced, race-safe warning box on the form. The warning never blocks submission; the officer still decides.
#go#mysql#react - 04:42backend
Four Contracts Behind a Public Upload Form
One multipart endpoint enforces four contracts: closed enums, conditional file slots, bounded streaming, and safe schema migration.
TL;DR: The upload form enforces four contracts: hand-maintained Go enums for validation, files that become required only when a flag says so, streaming writes capped at 10 MiB with magic-byte checks into private storage, and schema migrations that clean up enum values before narrowing on rollback. All four must stay synchronized server-side. Any drift breaks the endpoint predictably.
#go#multipart#file-upload - 04:42backend
Calendar-Day Flooring Is Not 24-Hour Truncation in Go
time.Truncate cuts by duration since the zero time, not to local midnight. Here is the safe pattern for DATE columns.
TL;DR: Go's time.Truncate(24 * time.Hour) counts multiples from Go's zero time, not local midnight, so early-morning timestamps on a UTC+7 server slip to the previous day. The MySQL driver's default UTC location conversion then locks the wrong day in. Fix: extract date components and rebuild with time.Date at UTC midnight.
#go#mysql#timezone - 01:34database
Flipping a Column Default: A Behavior Switch for New Rows
Changing one column DEFAULT is instant and metadata-only. The real work is auditing the INSERT paths whose meaning silently changed.
TL;DR: Changing a column default in MySQL is instant metadata-only, no table lock or rebuild. Existing rows keep their values; only INSERTs that omit the column flip behavior. Audit those callers first, then ship the migration.
#mysql#database-migration#schema - 00:24security
An Audit Trail That Survives Until Someone Asks
An append-only audit table written after each successful mutation, with no foreign key so rows outlive deleted accounts.
TL;DR: A security review found the app couldn't answer who changed a record, since app logs rotate and binlogs lack actor context. The fix is a dedicated audit_logs table written after successful admin mutations, append-only, with indexes and best-effort inserts that never break requests. Residual risk: a failed insert leaves a silent gap; metrics and alerting remain future work.
#audit-log#go#mysql