Working on a rails app with postgresql.
I am trying to implement a pg constraint using a case statement.
ALTER TABLE geolocation_postcodes
ADD CONSTRAINT canadian_format CHECK (
CASE WHEN country_code = 'CA' AND code !~ '\A^[A-Z]\d[A-Z]\s\d[A-Z]\d\Z' THEN FALSE
END
);
When trying to insert an instance (which should pass) it is stopped by the constraint every time:
Input:
Geolocation::Postcode.insert(code: "G6V 4C9", country_code: "CA", created_at: Time.now, updated_at: Time.now)
Result:
ActiveRecord::StatementInvalid: PG::CheckViolation: ERROR: new row for relation "geolocation_postcodes" violates check constraint "canadian_format" DETAIL: Failing row contains (46, G6V 4C9, t, 2021-04-07 01:37:34.005891, 2021-04-07 01:37:34.005896, CA).
There are a few issues here:
(1) Your regex is attempting to use both \A
(beginning of string) and ^
(beginning of line). You only want one of those and I'd go with \A
for consistency with \Z
and \A
in Ruby:
\A[A-Z]\d[A-Z]\s\d[A-Z]\d\Z
(2) You can fix this by renaming the constraint to something like postal_code_format
and restructuring the logic like:
code ~ case country_code
when 'CA' then '\A[A-Z]\d[A-Z]\s\d[A-Z]\d\Z'
when 'US' then ...
end
That structure makes the SQL case
look like a Ruby hash and keeps everything together and easy to read.
(3) This one is probably at the root of your problems. I'm guessing that you have something like this in a migration:
connection.execute(%Q(
ALTER TABLE geolocation_postcodes
ADD CONSTRAINT canadian_format CHECK (
CASE WHEN country_code = 'CA' AND code !~ '\A^[A-Z]\d[A-Z]\s\d[A-Z]\d\Z' THEN FALSE
END
);
))
or a heredoc or other double quoted string. All those strings will interpret backslashes so \A
will end up as A
, \d
as d
, ... and by the time your regex gets to the database it will look like:
A^[A-Z]d[A-Z] d[A-Z]dZ
and your CHECK will fail.
If you use a single quoted string and the right regex, things should be okay:
connection.execute(%q(
alter table geolocation_postcodes
add constraint postal_code_format check (
code ~ case country_code
when 'CA' then '\A[A-Z]\d[A-Z]\s\d[A-Z]\d\Z'
end
)
))
The logic inside the check constraint should be for the positive match cases. So I would use here:
ALTER TABLE geolocation_postcodes
ADD CONSTRAINT canadian_format CHECK (
(country_code = 'CA' AND code ~ '\A[A-Z][0-9][A-Z]\s[0-9][A-Z][0-9]\Z') OR
-- other possible matching country codes here
);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.