I am using SQLAlchemy and PostgreSQL, and I am trying to create a case-insensitive unique constraint that works like this
UniqueConstraint(
'country',
'organisation_id',
func.lower(func.trim('name')),
name='uq_country_orgid_lower_trim_name'
)
Ensuring a unique combination of name, country and organisation id, regardless of case and spacing in the name, i.e. "Name 1", "name1", "nAmE 1" would all be handled as "name1" in the check.
I want to make sure that I do not change the actual case or spacing of the name saved in the database.
How would I go about this?
3
Answers
You can use the
text
construct to declare an expression within an index declaration, liketext("""lower(replace(name, ' ', ''))""")
.An example script would look like this:
This is the generated index DDL:
Using the information from this answer we can see that the index contains this data:
showing that the indexed
name
is indeed lower-cased and free of spaces.Resources
I tend to avoid putting scripting this type requirement in SQLAlchemy (or any other obscurification [ORM] language). You cannot guarantee access will always be through that script. Rather direct ddl. One possibility is creating a generated column and a SQL function to create the single column for you index. This way you enforce the requirement at the lowest possible level thus ensuring it will always be employed. So, what is needed:
As an example:
With the above your constraint is always enforced even if another team shares the data but chooses another ORM or the DBA attempts an emergency change via direct SQL. See Example Here.
Yes, it is somewhat longer and makes the tradeoff of some storage for the requirement of never being bypassed. (Well a superuser can get around it, but that is always possible.)
This is mostly the ORM version of @snakecharmerb and avoids the use of
text
(nothing wrong with sanitized text, it is just a preference). This answer suggests using a unique index, instead of unique constraint because SQLA lacks support.