Postgres CHAR check constraint not evaluating correctly - postgresql

My goal is to have a column which accepts only values of the type letters and underscores in Postgres.
For some reason though I'm not managing to get Postgres to enforce the regex correctly.
Things I tried:
Produces column which won't accept strings with digits
action_type CHAR(100) check (action_type ~ '^(?:[^[:digit:]]*)$')
Produces column which won't accept anything
action_type CHAR(100) check (action_type ~ '^(?:[^[:digit:] ]*)$')
Produces column which won't accept anything
action_type CHAR(100) check (action_type ~ '^([[:alpha:]_]*)$'),
I have tried using multiple variations of the above as well as using SIMILAR TO instead of '~'. From my experience the column either accepts everything or nothing, depends on the given constraint.
I'm running this on the timescaledb docker image locally which is running PostgreSQL 12.5.
Any help would be greatly appreciated as I am at my wits end.

Try this:
CREATE TABLE your_table(
action_type text
);
ALTER TABLE your_table ADD CONSTRAINT check_letters
CHECK (action_type ~ '^[a-z\_]*$' )
This should only allow a-z characters and underscore _ for action_type column.
Also note that this is case sensitive, if you need case insensitive match, use ~* instead of ~
And also, this allows empty string, if you don't want that, use '^[a-z\_]+$' instead

Related

How would I diagnose what error seems to lead to non-functional underscore wildcard queries in Postgresql 15?

I am working through a quick refresher ('SQL Handbook' by Flavio Copes), and any LIKE or ILIKE query I use with the underscore wildcard returns no results.
The table is created as such:
CREATE TABLE people (
names CHAR(20)
);
INSERT INTO people VALUES ('Joe'), ('John'), ('Johanna'), ('Zoe');
Given this table, I use the following query:
SELECT * FROM people WHERE names LIKE '_oe';
I expect it to return
names
1
Joe
2
Zoe
Instead, it returns
names
The install is PostgreSQL 15 (x64), pgAdmin 4, and PostGIS v3.3.1
Using char(20) means all strings are exactly 20 chars long, being padded with spaces out to that length. The spaces make it not match the pattern, as there is nothing in the pattern to accommodate spaces at the end.
If you make the pattern be '_oe%' it would work. Or better yet, don't use char(20).

Table-wide constraint in Postgres

I'm rather new at Postgres. Is there any way that I can write a constraint for a table that checks ALL characters fields and tests to make sure that there are no leading or trailing characters IF there is any value in the field?
This way I don't have to itemize each and every character field when I write the constraint.
Thanks!
No, you cannot write such a constraint insofar as I am aware.
What you could do is to create a DOMAIN that has the check function and then make all of your table columns of that domain type. Assuming that the characters you refer to are spaces:
CREATE DOMAIN varchar_no_spaces AS varchar
CHECK ( left(VALUE, 1) <> ' ' AND right(VALUE, 1) <> ' ') );
There are many variations on this CHECK expression, including regular expression and using different or multiple characters. See the string functions for more options.
Then:
CREATE TABLE mytable (
f1 varchar_no_spaces,
...
);
Effectively you relay the constraint check to the level of the domain.

Postgresql constraint to check for non-ascii characters

I have a Postgresql 9.3 database that is encoded 'UTF8'. However, there is a column in database that should never contain anything but ASCII. And if non-ascii gets in there, it causes a problem in another system that I have no control over. Therefore, I want to add a constraint to the column. Note: I already have a BEFORE INSERT trigger - so that might be a good place to do the check.
What's the best way to accomplish this in PostgreSQL?
You can define ASCII as ordinal 1 to 127 for this purpose, so the following query will identify a string with "non-ascii" values:
SELECT exists(SELECT 1 from regexp_split_to_table('abcdéfg','') x where ascii(x) not between 1 and 127);
but it's not likely to be super-efficient, and the use of subqueries would force you to do it in a trigger rather than a CHECK constraint.
Instead I'd use a regular expression. If you want all printable characters then you can use a range in a check constraint, like:
CHECK (my_column ~ '^[ -~]*$')
this will match everything from the space to the tilde, which is the printable ASCII range.
If you want all ASCII, printable and nonprintable, you can use byte escapes:
CHECK (my_column ~ '^[\x00-\x7F]*$')
The most strictly correct approach would be to convert_to(my_string, 'ascii') and let an exception be raised if it fails ... but PostgreSQL doesn't offer an ascii (i.e. 7-bit) encoding, so that approach isn't possible.
Use a CHECK constraint built around a regular expression.
Assuming that you mean a certain column should never contain anything but the lowercase letters from a to z, the uppercase letters from A to Z, and the numbers 0 through 9, something like this should work.
alter table your_table
add constraint allow_ascii_only
check (your_column ~ '^[a-zA-Z0-9]+$');
This is what people usually mean when they talk about "only ASCII" with respect to database columns, but ASCII also includes glyphs for punctuation, arithmetic operators, etc. Characters you want to allow go between the square brackets.

Taking the prefix of a column value in PostgreSQL

I want to select a column value and trim away a suffix matching a regular expression, all inside PostgreSQL, how can this be done?
-- somewhat like performing s/_bar$// on "foo_bar"
select regexp_replace('foo_bar', '_bar$', '')

Omitting the double quote to do query on PostgreSQL

Simple question, is there any way to omit the double quote in PostgreSQL?
Here is an example, giving select * from A;, I will retrieve ERROR: relation "a" does not exist, and I would have to give select * from "A"; to get the real result.
Is there any way not to do the second and instead do the first on PostgreSQL?
Your problem with this query started when you created your table. When you create your table, don't use quotes.
Use this:
CREATE TABLE a ( ... );
Not this:
CREATE TABLE "A" ( ... );
The latter will make it so that you always have to quote it later. The former makes it a normal name and you can use SELECT * FROM a; or SELECT * FROM A;
If you can't just recreate your table, use the ALTER TABLE syntax:
ALTER TABLE "A" RENAME TO a;
double quotes are required if you include capital letters in your table name in postgres
to avoid the requirements name your table "a"
Postgresql has some particular behaviour in regard to quoting and case sentivity: it folds every non-quoted identifier to lower case (also at creation time) and then works case-sensitively.
Double quotes in identifiers are only needed when the identifier (table name, column name, etc) was defined (at schema creation time) with uppercase letters (some or all) and between double quotes.
In that case (which I advice against), when you use that identifier, you must type it in the same way: case sensitively (type upper/lower case letter exactly as defined) and between double quotes.
In other cases, you can use non-quoted identifiers and work always case-insensitively.
Don't use upper case letter in your table name or it's column name, if you are using such thing then the postgres will required double quote for accessing it.
Please see the detailed description of what is happening here.
The PostgreSQL server table names are case-sensitive, but forced to be lower-case by default: when you type CREATE TABLE AAA, it will become CREATE TABLE aaa before the query execution.
Double-quoted names keep their case as it was, so after CREATE TABLE "AaA" you get the table AaA and have to write it double-quoted again and again.
Have no idea why did they do so :)