Suriya f343f4e86e Let the identity column allocate userid, instead of computing it
`app_users.userid` is a `GENERATED BY DEFAULT AS IDENTITY` column. It did not
look like one: `information_schema.columns.column_default` is empty for identity
columns, and reading that as "no default at all" is how this came to compute its
own id with MAX+1.

That worked, and quietly did the wrong thing. An explicit id does not advance
the sequence, so two allocators ended up running in parallel — the sequence sat
at 1447 while MAX(userid) had reached 9189. They cannot collide today, because
almost nothing occupies the range between, but they converge on every insert and
the first collision would be a primary key violation on a live sign-up.

The insert now omits userid and reads it back with RETURNING. The advisory lock
stays, because it was never about the id: two supervisors adding staff at the
same instant could both find a PIN free and both take it, and a duplicate PIN
attributes a bill to whichever row is read first.

Also documents why the email columns go through NULLIF. `app_users_email_unique`
is real, and a second cashier created without an email would otherwise collide
on the empty string — while NULLs do not collide in Postgres. A cashier who
signs in by PIN alone has no email, which is the common case rather than the
edge one.

Verified in a rolled-back transaction against live data: creation allocates
1448, the sequence advances 1447 -> 1448, a second emailless user is accepted,
and a duplicate PIN is still refused.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 20:30:18 +05:30
2026-05-25 11:52:26 +05:30
2026-08-03 17:56:09 +05:30
2026-07-04 17:49:54 +05:30
2026-07-09 18:03:44 +05:30
2026-08-03 20:22:11 +05:30
pos
2026-08-03 20:19:04 +05:30
Description
No description provided
18 MiB
Languages
Go 99.4%
JavaScript 0.6%