FLC
FLC

Reputation: 895

Is it possible to create a char column which is always lowercase?

I want to create a table like:

create table project_types (
  id char(20) not null unique default 'xxx'
};

To use it from other tables as:

create table other_table (
  ...
  fk_ptype char(20),
  fk_ptype_on_other_table" foreign key (fk_ptype) references project_type(id)
);

The catch is I'd want all values inserted into project_types to become automatically lowercase: I don't want to be making the conversion on each possible query, I want a table that no matter what I throw at it, it returns back lowercase tokens.

I'm thinking about making a trigger on insert an on update, but I'm wondering if there's a better way to impose such a restriction. Also, this solution means I'll have to make the conversion on deletions.

For the ones that may suggest that I do this with enums: the types are dynamic, so I prefer this approach.

UPDATE 2017.04.17: The idea of this question is not to put controls/transformations everywhere in the stack: if database can handle whatever you throw at it, then you don't have to 1. check/transform in the front-end, 2. check/transform in the back-end code, and finally 3. check/transform in the database. You just avoid doing 1 and 2 because you know database will handle whatever you throw at it and that you'll have correct data when you select from it.

I'm tempted to choose @herbert-pimentel answer, but it seems the same approach cannot bo used for delete (I tried setting and on-delete trigger using the same function but it didn't work).

Upvotes: 1

Views: 386

Answers (3)

joanolo
joanolo

Reputation: 6328

You can use a special type of data for this purpose, called CITEXT (=case insensitive text). It is an additionally supplied module standard within PostgreSQL.

Citing the PostgreSQL documentation on CITEXT:

F.8.1. Rationale

The standard approach to doing case-insensitive matches in PostgreSQL has been to use the lower function when comparing values, for example

SELECT * FROM tab WHERE lower(col) = LOWER(?);

This works reasonably well, but has a number of drawbacks:

It makes your SQL statements verbose, and you always have to remember to use lower on both the column and the query value.

It won't use an index, unless you create a functional index using lower.

If you declare a column as UNIQUE or PRIMARY KEY, the implicitly generated index is case-sensitive. So it's useless for case-insensitive searches, and it won't enforce uniqueness case-insensitively.

The citext data type allows you to eliminate calls to lower in SQL queries, and allows a primary key to be case-insensitive. citext is locale-aware, just like text, which means that the matching of upper case and lower case characters is dependent on the rules of the database's LC_CTYPE setting. Again, this behavior is identical to the use of lower in queries. But because it's done transparently by the data type, you don't have to remember to do anything special in your queries.

So, in your specific case, you just would need to do:

One time:

CREATE EXTENSION citext ;

CREATE TABLE project_types 
(
    id citext PRIMARY KEY default 'xxx'
);

CREATE TABLE other_table 
(
     /* ... */
   fk_ptype citext,
   fk_ptype_on_other_table foreign key (fk_ptype) references project_type(id)
);

... and then, do nothing to your queries. Don't have any extra constraints and don't have any (apparently feared) trigger.

Upvotes: 1

Clodoaldo Neto
Clodoaldo Neto

Reputation: 125544

Check constraint:

create table project_types (
    id char(20) not null unique default 'xxx'
    check (id = lower(id))
);

Upvotes: 2

Herbert Pimentel
Herbert Pimentel

Reputation: 425

how about a trigger with before insert or update to ensure/transform your data lower case;

CREATE OR REPLACE FUNCTION public.fun_trg_lowercase()
  RETURNS trigger AS
$BODY$
begin 
  NEW.my_char_field = lowercase(NEW.my_char_field);  
  RETURN NEW;
end;
$BODY$
  LANGUAGE plpgsql VOLATILE;

CREATE TRIGGER biu_lowercase_field
  BEFORE INSERT OR UPDATE
  ON mytable
  FOR EACH ROW
  EXECUTE PROCEDURE fun_trg_lowercase();

Upvotes: 3

Related Questions