Back to Feed
article 3m read

A login that knew only one spelling of the column it searches on

Some of my databases name the lookup key column differently, and since SQL won't bind a column name, I pick it from a two-item list in the code by asking the schema, then let each caller decide what a miss means.

Article DepthPractice

Flow

Adding a name-picking helper, in order

  1. 1Write the two names in code

    The list lives in one place and takes nothing from users.

  2. 2Check the table, in a fixed order

    Return the first name the database confirms.

  3. 3Route the queries through it

    Login, the update page, listing, saving and lookup by email.

  4. 4Paste only the name

    Every value still goes through a placeholder.

  5. 5Remember hits, not misses

    An upgraded database is noticed on the next request.

  6. 6Decide each caller's miss

    Login errors, the update page falls back, setup refuses.

The login in the ordering system knew one name for the column it searches on. Some of my databases used a different name.

Emails in the user table are encrypted, so login can't search them. It searches a hash of the email instead, kept in its own column. Some of my databases name that column in camel case. Others use an underscore. Login only looked for camel case, and if it wasn't there, login stopped with a server error. On an underscore database, that's a broken login on a table that was fine.

Values fit in a placeholder, names don't

Using whichever name exists sounds easy, but look at where the name goes. A placeholder in a query can carry a value, like the hash you're looking for. It can't carry a column name. So when the column name changes, it has to be pasted into the SQL text. I don't like pasting text into SQL, so the name has to come from somewhere safe.

It comes from a short list written in the code. A small helper asks the database which of the two names the table really has, and returns that one:

KNOWN = ("lookupKey", "lookup_key")

The helper takes no input from the user. It can only ever return one of those two strings. That's what makes the pasting safe. If that list ever turns into a setting, or picks up anything a user typed, it isn't safe anymore.

Who uses the helper, and what happens on a miss

Nearly every query on the hash now asks the helper for the name: login, the update page, listing users, saving a user, and finding one by email. Only the name is pasted in. Every value still goes through a placeholder.

A good answer is remembered for the rest of the request. A miss is not, so the next call looks again. That way a database that gets upgraded is noticed on the next request.

When neither name exists, each caller does its own thing. Login logs it and returns a server error, which I'd take over guessing at a sign-in. The update page falls back to an older lookup, and I suspect that's on purpose, but it logs nothing when it does. First-run setup refuses to make the first admin if neither column is there, which is right, though it runs its own copy of the check rather than calling the helper.

Keep reading

Pick up a connected idea or branch into a nearby one.

5 paths forward