Moving "Craig's" gapless sequence to other schema fails? - postgresql

While trying to move Craig Ringer's PostgreSQL gapless sequences example in to another schema 'test' I get an error. The query
INSERT INTO test.dummy(id, blah)
VALUES ( test.get_next_id('test.thetable_id_counter','last_id'), 42 );
errors with
relation "test.thetable_id_counter" does not exist
UPDATE "test.thetable_id_counter" SET "test.last_id" = "test...
^
yet the query:
SELECT to_regclass('test.thetable_id_counter');
does not result in NULL.

Related

SELECT from result of UPDATE ... RETURNING in jOOQ

I'm transforming some old PostgreSQL code to jOOQ, and I'm currently struggling with SQL that has multiple WITH clauses, where each one depends on previous. It would be best to keep the SQL logic the way it was written and not to change it (e.g. multiple queries to DB).
As it seems, there is no way to do SELECT on something that is UPDATE ... RETURNING, for example
dsl.select(DSL.asterisk())
.from(dsl.update(...)
.returning(DSL.asterisk())
)
I've created some test tables, trying to create some sort of MVCE:
create table dashboard.test (id int primary key not null, data text); --test table
with updated_test AS (
UPDATE dashboard.test SET data = 'new data'
WHERE id = 1
returning data
),
test_user AS (
select du.* from dashboard.dashboard_user du, updated_test -- from previous WITH
where du.is_active AND du.data = updated_test.data
)
SELECT jsonb_build_object('test_user', to_jsonb(tu.*), 'updated_test', to_jsonb(ut.*))
FROM test_user tu, updated_test ut; -- from both WITH clauses
So far this is my jOOQ code (written in Kotlin):
dsl.with("updated_test").`as`(
dsl.update(Tables.TEST)
.set(Tables.TEST.DATA, DSL.value("new data"))
.returning(Tables.TEST.DATA) //ERROR is here: Required Select<*>, found UpdateResultStep<TestRecord>
).with("test_user").`as`(
dsl
.select(DSL.asterisk())
.from(
Tables.DASHBOARD_USER,
DSL.table(DSL.name("updated_test")) //or what to use here?
)
.where(Tables.DASHBOARD_USER.IS_ACTIVE.isTrue
.and(Tables.DASHBOARD_USER.DATA.eq(DSL.field(DSL.name("updated_test.data"), String::class.java)))
)
)
.select() //here goes my own logic for jsonBBuildObject (which is tested and works for other queries)
.from(
DSL.table(DSL.name("updated_test")), //what to use here
DSL.table(DSL.name("test_user")) //or here
)
Are there any workarounds for this? I'd like to avoid changing SQL if possible.
Also, in this project this trick is used very often to get JSON(B) from UPDATE clause (table has JSON(B) columns too):
with _updated AS (update dashboard.test SET data = 'something' WHERE id = 1 returning *)
select to_jsonb(_updated.*) from _updated;
and it will be a real step back for us if there is no workaround for this.
I'm using JOOQ version 3.13.3, and Postgres 12.0.
This is currently not supported in jOOQ, see:
https://github.com/jOOQ/jOOQ/issues/3185
https://github.com/jOOQ/jOOQ/issues/4474
The workaround is, as always, when some vendor specific syntax is unsupported, to resort to plain SQL templating
E.g.
// If you don't need to map data types
dsl.fetch("with t as ({0}) {1}", update, select);
// If you need to map data types
dsl.resultQuery("with t as ({0}) {1}", update, select).coerce(select.getSelect()).fetch();

Postgres Update Using Select Passing In Parent Variable

I need to update a few thousand rows in my Postgres table using the result of a array_agg and spatial lookup.
The query needs to take the geometry of the parent table, and return an array of the matching row IDs in the other table. It may return no IDs or potentially 2-3 IDs.
I've tried to use an UPDATE FROM but I can't seem to pass into the subquery the parent table geom column for the SELECT. I can't see any way of doing a JOIN between the 2 tables.
Here is what I currently have:
UPDATE lrc_wales_data.records
SET lrc_array = subquery.lrc_array
FROM (
SELECT array_agg(wales_lrcs.gid) AS lrc_array
FROM layers.wales_lrcs
WHERE st_dwithin(records.geom_poly, wales_lrcs.geom, 0)
) AS subquery
WHERE records.lrc = 'nrw';
The error I get is:
ERROR: invalid reference to FROM-clause entry for table "records"
LINE 7: WHERE st_dwithin(records.geom_poly, wales_lrcs.geom, 0)
Is this even possible?
Many thanks,
Steve
Realised there was no need to use SET FROM. I could just use a sub query directly in the SET:
UPDATE lrc_wales_data.records
SET lrc_array = (
SELECT array_agg(wales_lrcs.gid) AS lrc
FROM layers.wales_lrcs
WHERE st_dwithin(records.geom_poly, wales_lrcs.geom, 0)
)
WHERE records.lrc = 'nrw';

Why can't I use WHERE NOT EXISTS with INSERT?

I want to insert a tag named "foo", unless it already exists. So I constructed the following query:
INSERT INTO "tag" ("name") VALUES ('foo')
WHERE NOT EXISTS (SELECT 1 FROM "tag" WHERE ("tag"."name" = 'foo'));
But this will fail with the following error:
ERROR: syntax error at or near "WHERE"
LINE 1: INSERT INTO "tag" ("name") VALUES ('foo') WHERE NOT EXISTS (...
^
I don't understand where the problem with that query is. Especially, since I can provide a subquery instead of VALUES and suddenly the query is perfectly fine:
INSERT INTO "tag" ("name") SELECT 'foo' AS name
WHERE NOT EXISTS (SELECT 1 FROM "tag" WHERE ("tag"."name" = 'foo'));
This results in:
Query returned successfully: 0 rows affected, 11 ms execution time.
It's 0 rows, because the tag already exists.
You can, you just need to use the INSERT INTO ... SELECT ... form.
INSERT INTO "tag" ("name")
SELECT 'foo'
WHERE NOT EXISTS (SELECT 1 FROM "tag" WHERE ("tag"."name" = 'foo'));
However, it doesn't do what you want. At least not under concurrent workloads. You can still get unique violations or duplicate inserts.
See:
How to UPSERT (MERGE, INSERT ... ON DUPLICATE UPDATE) in PostgreSQL?
Insert, on duplicate update in PostgreSQL?

Fixing column "columnname" does not exist pgsql in database. Double quote vs single quote error

I have a table review(movie_id, user_id, reviewtext, date, time, likes, status)/
I get the error
column "exist" does not exist LINE 1: INSERT INTO review values ($1, $2, $3,$4,$5 ,0,"exist") ^ )
when I want to insert values into a postgresql database. I can not modify the code anymore so is there any way to make this work by altering the database like adding a column?
The code to insert is as follows:
$query = $this->db->prepare('INSERT INTO review values (:movieid, :userid, :review,:date,:time ,0,"exist")');
$result = $query->execute(Array(':movieid' => $movieid, ':userid' => $userid, ':review' => $review, ':date' => $date, ':time' => $time));
I understand that a way to fix this is to use single quotes for the column 'status' but the only thing I can do is alter the database.
No you can't.
If you had used proper insert - with named columns:
insert into review (column1, column2, column3) values (....)
then it could be theoretically possible to do by adding column "exist" and a trigger. But this would be very far away from being sane solution.

Postgres query error

I have a query in postgres
insert into c_d (select * from cd where ak = '22019763');
And I get the following error
ERROR: column "region" is of type integer but expression is of type character varying
HINT: You will need to rewrite or cast the expression.
An INSERT INTO table1 SELECT * FROM table2 depends entirely on order of the columns, which is part of the table definition. It will line each column of table1 up with the column of table2 with the same order value, regardless of names.
The problem you have here is whatever column from cd with the same order value as c_d of the table "region" has an incompatible type, and an implicit typecast is not available to clear the confusion.
INSERT INTO SELECT * statements are stylistically bad form unless the two tables are defined, and will forever be defined, exactly the same way. All it takes is for a single extra column to get added to cd, and you'll start getting errors about extraneous extra columns.
If it is at all possible, what I would suggest is explicitly calling out the columns within the SELECT statement. You can call a function to change type within each of the column references (or you could define a new type cast to do this implicitly -- see CREATE CAST), and you can use AS to set the column label to match that of your target column.
If you can't do this for some reason, indicate that in your question.
Check out the PostgreSQL insert documentation. The syntax is:
INSERT INTO table [ ( column [, ...] ) ]
{ DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] ) | query }
which here would look something like:
INSERT INTO c_d (column1, column2...) select * from cd where ak = '22019763'
This is the syntax you want to use when inserting values from one table to another where the column types and order are not exactly the same.