05 May 2015
Using database columns as the golden source of constraints with Clojure
I’ve recently been working on data input on a project using Clojure and PostgreSQL. The database columns were
created, specifying maximum column sizes on columns such as username
and password
:
It’s been a while since I’ve used a SQL database in anger, and there is something very reassuring about
having a reliable backing store that will not let any bad data in. However, I found
that I had these max-length properties of 50
for username and 100
for email in several places:
- the database DDL scripts
- controllers, used to validate on the server side
- the login HTML page, setting
maxlength
attributes oninput
tags - the registration page
- the forgotten-password page
After a bug was found that was caused by the value being wrong in one of those 5 places, I decided that there must be a better approach.
The approach in a nutshell
The database is the best golden source for something like max-column lengths, because that’s where the data lives. By enforcing in the database, it doesn’t matter how bug-ridden your software is, no bad data will get in. So, the approach is this:
- On application startup, load column meta data from the database for the current schema
- Create a mapping of tables and columns to their various constraint values
- Query this mapping from code to get values, and associate the map with the HTML templating engine for use in HTML.
Querying the data and creating the map
The following SQL will give you lots of information on the columns in your database for a schema named public
(change
this to whatever your schema is), and although tested only in PostgreSQL should work in most database servers:
Of interest for max column lengths are the columns table_name
, column_name
, and character_maximum_length
. By querying
just those 3 columns, it’s quite trivial to build up a map in clojure containing all the tables and columns in your database:
Note that this assumes there is a DB function called query
that returns a list of column-value pairs for a SQL query.
All this does is query the column info, then add values keyed by table name, column name, and constraint type. Here is a
test showing example usage from Clojure:
Of course, you might want to make it a bit easier:
To use this from an HTML templating engine, one approach is to simply associate the whole constraints
map with your
templating library. For example, you may have a place where some default values are assigned to the model, in which case
you can just associate :constraints constraints
and then use it from any HTML page. For example, the following sets
the maxlength
attribute on the login page:
Now if we need to increase or decrease the length of something like the username, we just change the column length in the database and all the validation in the HTML and server-side code will be updated.
Taking it further
It would also be possible to check if columns were nullable or not, and use this to figure out if fields are required
or not. More interesting is the fact that check constraint
values in many DBs support regexes. You could extract the
check constraint
value from the DB, and then associate it with the pattern
attribute on input
fields so that you
don’t have duplicate regexes throughout your code.