Set-returning functions are not allowed in UPDATE when using Postgres 10 - postgresql

We have an old Flyway database update
UPDATE plays SET album = (regexp_matches(album, '^6,(?:(.+),)?tv\d+'))[1]
...that runs fine with any Postgres version from 9.2 to 9.6 but fails with latest Postgres 10. Happens even when ran directly without any JDBC.
ERROR: set-returning functions are not allowed in UPDATE
Is there a backwards incompatibility I didn't notice from version 10 release notes? Is there a workaround?

This is untested, but should work in all PostgreSQL versions:
UPDATE plays SET album = substring (album FROM '^6,(?:(.+),)?tv\d+');

I had a more general problem where I needed the second match from a regex.
The solution was a nested subselect
SET my_column = (SELECT a.matches[2] from
(SELECT regexp_matches(my_column, '^(junk)?(what_i_want)$') matches) a)
Or modify the regex to return one group and apply #LaurenzAlbe 's answer:
SET my_column = substring (my_column FROM '^junk?(what_i_want)$')
There may be cases where modifying the regex is not ideal.
The original was of the form
SET my_column = regexp_matches(my_column, '^(junk)?(what_i_want)$')[2]
Where junk and what_i_want were fairly complex rexex fragments.

Related

Postgresql UPDATE query works on Windows but not on Ubuntu

The following query executed on postgresql v10.3 running on windows works as expected. Suppose the fees_balance was 10000 before running the query, the query below gives back 17000:
UPDATE tbl_classes SET
fees_balance=T2.fees_balance + 7000
FROM tbl_classes T2 WHERE CAST(tbl_classes.mobile_number AS TEXT)
LIKE '%71000001' RETURNING tbl_classes.fees_balance;
Now the same query executed on postgresql v10.1 running on ubuntu 18.04 gives -7000! Is this a bug in postgresql on ubuntu or am I missing something? Kindly help
You have a FROM clause, but never refer to the table in it in the WHERE clause. So every row of T2 meets the criteria.
That means that the specified row(s) of "tbl_classes" is going to be set to 7000 more than the value of some arbitrary row of T2 (which is also "tbl_classes" just under a different alias, but it has its own "row pointer")
Apparently on Windows the arbitrary row happens to be the one you want, while on Linux it is not. This is just dumb luck. You would only use a FROM clause here if you wanted to refer to two different rows of the same table in deriving the value to set. That does not seem to be the case.
UPDATE tbl_classes SET
fees_balance=fees_balance + 7000
WHERE CAST(tbl_classes.mobile_number AS TEXT)
LIKE '%71000001' RETURNING tbl_classes.fees_balance;

Postgresql ignoring 'when' condition on trigger

A trigger seems to be ignoring the 'when condition' in my definition but I'm unsure why. I'm running the following:
create trigger trigger_update_candidate_location
after update on candidates
for each row
when (
OLD.address1 is distinct from NEW.address1
or
OLD.address2 is distinct from NEW.address2
or
OLD.city is distinct from NEW.city
or
OLD.state is distinct from NEW.state
or
OLD.zip is distinct from NEW.zip
or
OLD.country is distinct from NEW.country
)
execute procedure entities.tf_update_candidate_location();
But when I check back in on it, I get the following:
-- auto-generated definition
create trigger trigger_update_candidate_location
after update
on candidates
for each row
execute procedure tf_update_candidate_location();
This is problematic because the procedure I call ends up doing an update on the same table for different columns (lat/lng). Since the 'when' condition is ignored this crates an infinite loop.
My intention is to watch for address change, do a lookup on another table to get lat/lng values.
Postgresql version: 10.6
IDE: DataGrip 2018.1.3
How exactly do you create and "check back"? With datagrip?
The WHEN condition was added with Postgres 9.0. Some old (or poor) clients may be outdated. To be sure, check in pgsql with:
SELECT pg_get_triggerdef(oid, true)
FROM pg_trigger
WHERE tgrelid = 'candidates'::regclass -- schema-qualify name to be sure
AND NOT tgisinternal;
Any actual WHEN qualification is stored in internal format in pg_trigger.tgqual, btw. Details in the manual here.
Also what's your current search_path and what's the schema of table candidates?
It stands out that the table candidates is unqualified, while the trigger function entities.tf_update_candidate_location() has a schema-qualification ... You are not confusing tables of the same name in different DB schemas, are you?
Aside, you can simplify with this shorter, equivalent syntax:
create trigger trigger_update_candidate_location
after update on candidates -- schema-qualify??
for each row
when (
(OLD.address1, OLD.address2, OLD.city, OLD.state, OLD.zip, OLD.country)
IS DISTINCT FROM
(NEW.address1, NEW.address2, NEW.city, NEW.state, NEW.zip, NEW.country)
)
execute procedure entities.tf_update_candidate_location();
Unfortunately, that's the issue of DataGrip. Please follow the ticket to be notified when it's fixed.
https://youtrack.jetbrains.com/issue/DBE-7247

cakephp condition using '?' in the string [duplicate]

For detecting the existence of a key in a hstore, I need to run a query like this:
SELECT * FROM tbl WHERE hst ? 'foo'
However, that gives me a PDOException:
PDOException: SQLSTATE[HY093]: Invalid parameter number: no parameters were bound: SELECT * FROM tbl WHERE hst ? 'foo'
Is there any way to escape the question mark so PDO won't pick it up as a placeholder? I've tried with up to four backslashes, as well as a double question mark (??), but nothing seems to persuade PDO to leave the question mark alone.
Use the function call form. According to the system catalogs, the hstore ? operator uses the exist function:
regress=# select oprname, oprcode from pg_operator where oprname = '?';
oprname | oprcode
---------+---------
? | exist
(1 row)
so you can write:
SELECT * FROM tbl WHERE exist(hst,'foo');
(Personally I'm not a big fan of hstore's operator-centric design and documentation, I think it discards the useful self-documenting properties of a function based interface without any real benefit and I usually use its function calls rather than its operators. Just because you can define operators doesn't mean you should.)
I had the same problem when searching on JSONB data. The full question is here
SELECT * FROM post WHERE locations ? :location;
The workaround on PostgreSQL 9.5 is similar:
SELECT * FROM post WHERE jsonb_exists(locations, :location);
I also opened a ticket at PHP bug tracing system
Update
As Diabl0 mentioned, the proposed solution work but does not use the index.
Tested with:
CREATE INDEX tempidxgin ON post USING GIN (locations);
I suggest you disable PDO native prepared statement so question marks will be ignored:
$pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, true);

Query works in postgresql, but not in hsqldb

column_name is of type int[]
SELECT unnest(column_name) FROM table_name
The above query works on postgresql but not on hsqldb, even with sql.syntax_pgs=true
Hsqldb versions tried : 2.2.9 and 2.3.0
The sql that works in hsqldb is
SELECT x FROM table_name, unnest(column_name) y(x)
x and y are NOT columns of this table.
HSQLDB tries to emulate PostgreSQL's syntax and features, but like most emulations it is imperfect.
IIRC, one of the things it has a hard time with is PostgreSQL's quirky use of set-returning functions in the SELECT clause.
Use of SRFs in the SELECT clause is a weird PostgreSQL extension that's deprecated in favour of SQL-standard LATERAL queries anyway. The alternate formulation you showed:
SELECT x FROM table_name, unnest(column_name) y(x);
is the correct and preferred form. So just use that.
In general, testing on one DB then deploying to another is a recipe for pain. I strongly suggest just setting up a local PostgreSQL instance for testing instead.

Fast search within strings in PostgreSQL

Which is the fastest way to search within string in PostgreSQL (case insensivity):
SELECT col FROM table WHERE another_col ILIKE '%typed%'
or
SELECT col FROM table WHERE another_col ~* 'typed'
How can I turn on showing the time which query need to return results? Something like is on default in mySQL (I am thinking about CLI client).
Both queries are the same, PostgreSQL rewrites ILIKE to ~*. Check the results from EXPLAIN to see this behaviour.
I'm not sure about your question, but the psql-client can show you some timing of the query, using \timing.
Regarding the timing:
One solution is to use the switch for psql that Frank has already mentioned.
When you use EXPLAIN ANALZYE it also includes the total runtime of the query on the server.
I prefer this when comparing the runtime for different versions of a query as it removes the network from the equation.