postgres condition trigger missing from clause - postgresql

I'm trying to create a trigger on my table such that it only runs if the 'prepaid' column is true for rows where I've modified the value of the 'points_per_month' column. I tried this:
CREATE TRIGGER "fix_usage_trigger"
AFTER UPDATE OF "points_per_month"
ON "public"."clients"
FOR EACH ROW WHEN (ROW.prepaid)
EXECUTE PROCEDURE "fix_prepaid_client_available_usage"();
psql is telling me this:
ERROR: missing FROM-clause entry for table "row"
LINE 1: ...r_month" ON "public"."clients" FOR EACH ROW WHEN (ROW.prepai...
Clearly I have no FROM clause there, but I'm not sure why I'd need one, nor where to put it.

That should be when (new.prepaid), per David's comment. You can access old and new in the when clause (as in the row before and after the update) much like table aliases. The error message is PG complaining that row is not a known table.
Two additional notes:
it might need to be when (old.prepaid or new.prepaid) if you want to manage billing plan switches -- or another two separate triggers. Conversely, when (old.prepaid and new.prepaid) if you do not, and someone might run database queries that might inadvertently fire the trigger and create undesirable state (add a unit test or two).
the function's name suggest something might be wrong further up in your code flow. You might want to fix that instead, by setting the available usage properly to begin with. Doing so might be more efficient, too.

Related

COPY support with postgreSQL v12 triggers

We have this pair of trigger and function that we use on our psql database for the longest time. Basically, the trigger is called each time there is a new record to the main table, and each row is inserted to the monthly partition individually. Following is the trigger function:
CREATE TRIGGER partition_mic_teams_endpoint_trg1
BEFORE INSERT ON "mic_teams_endpoint"
FOR EACH ROW EXECUTE
PROCEDURE trg_partition_mic_teams_endpoint('month');
The function we have creates monthly partitions based on a timestamp field in each row.
I have two questions:
List item Even if I try to COPY a bunch of rows from CSV to the main table, is this trigger/function going to insert each row individually? Is this efficient?
If that is the case, is it possible to have support for COPYing data to partitions instead of INSERT.
Thanks,
Note: I am sorry if I did not provide enough information for an answer
Yes, a row level trigger will be called for each row separately, and that will make COPY quite a bit slower.
One thing you could try is a statement level AFTER trigger that uses a transition table, so that you can
INSERT INTO destination SELECT ... FROM transition_table;
That should be faster, but you should test it to be certain.
See the documentation for details.

PostgreSQL select query on table that is being updated

I assume this question has been asked before, but unfortunately I cannot find the answer to my question.
I have a table, and I am using an update statement to update a column. Simultaneously I am running a create table query with a select statement that is retrieving data from the table and column that is also being updated.
My questions are: can this lead to wrong results in the output of the create table statement? does the update query finish 1st then the create table with the select execute? I just know that the create table statement is taking way longer to execute.
In PostgreSQL readers never lock writers and vice versa. This is guaranteed by PostgreSQL's MVCC implementation that keeps old row versions around.
If the updating transaction isn't finished yet, the reading transaction will see the old value, and the result is consistent.
There is nothing inside PostgreSQL that should slow down the SELECT statement noticeably, but of course I/O contention is a possible explanation.

Is it possible to perform an upsert that requires filtering in Postgres?

I'm wondering if it's possible to use the following statement to do an upsert w/ filtering. That is, can I first try to update with a where clause, if it fails, then insert, rather than the other way around? I would like to do this in Postgres.
INSERT ... ON CONFLICT DO NOTHING/UPDATE
I did see this, but it is definitely a bit more complicated
https://dba.stackexchange.com/questions/13468/idiomatic-way-to-implement-upsert-in-postgresql
That is, can I first try to update with a where clause, if it fails, then insert, rather than the other way around?
It's unclear why you would want to do this.
The purpose of UPSERT is to ensure that the database contains exactly one row with a given key and with a given set of other column values. Postgres tries INSERT first because INSERT will fail when the key conflicts with a duplicate row (so that it can fall back to updating the conflicting row instead of raising an exception). UPDATE will not fail if the WHERE clause matches nothing. It will successfully update zero rows. UPDATE can fail if you violate a constraint (e.g. a CHECK or NOT NULL constraint), but it won't fail just because you didn't match any rows.
And, on the other hand, if your UPDATE would change an existing row, then your INSERT would necessarily fail with a uniqueness violation (because the row exists). So trying the INSERT first doesn't actually change the result in this case.
It is possible to hang a condition on PostgreSQL's UPSERT, with syntax of the form INSERT... ON CONFLICT DO UPDATE... WHERE.... This will:
Insert the rows you provide.
For each conflict with an existing row, evaluate the WHERE condition for that row.
If the WHERE condition is satisfied, update the existing row, otherwise do nothing with it.
I believe this is functionally equivalent to what you are asking for, because:
If the row does not exist, Postgres will INSERT it. UPDATE wouldn't have affected it, so your method would have had to fall back to INSERTing it anyway.
If the row exists, but does not match the WHERE clause, then Postgres will do nothing. I think your method would either do nothing or fail with a uniqueness constraint after trying to INSERT it, but perhaps you had something else in mind for this case.
If the row exists and matches the WHERE clause, both Postgres and your method will do an UPDATE on that row.

Can Postgres silently ignore column constraint conflicts?

I have a Postgres 9.6 table with certain columns that must be unique. If I try to insert a duplicate row, I want Postgres to simply ignore the insert and continue, instead of failing or aborting. If the insert is wrapped in a transaction, it shouldn't abort the transaction or affect other updates in the transaction.
I assume there's a way to create the table as described above, but I haven't figured it out yet.
Bonus points if you can show me how to do it in Rails.
This is possible with the ON CONFLICT clause for INSERT:
The optional ON CONFLICT clause specifies an alternative action to
raising a unique violation or exclusion constraint violation error.
For each individual row proposed for insertion, either the insertion
proceeds, or, if an arbiter constraint or index specified by
conflict_target is violated, the alternative conflict_action is taken.
ON CONFLICT DO NOTHING simply avoids inserting a row as its
alternative action.
This is a relatively new feature and only available since Postgres 9.5, but that isn't an issue for you.
This is not something you specific at table creation, you'll need to modify each insert. I don't know how this works with Rails, but I guess you'll have to manually write at least part of the queries to do this.
This feature is also often called UPSERT, which is probably a better term to search for if you want to look for an integrated way in Rails to do this.

Incorrect syntax near the keyword 'truncate'. within a View

I get the error "Incorrect syntax near the keyword 'truncate'." and not sure what's wrong with the syntax here, it's not obvious to me...probably something stupid but I need another set of eyes:
ALTER VIEW [dbo].[vw_All_Events]
AS
truncate table Event
Select [event_id]
,[site_id]
,[autogenerated]
,[creation_date]
,[last_update_date]
from Event
GO
A view only allows a single statement after the AS and it must be data retrieval (return a rowset). It can't be a different type including data definition, data modification, procedural, declarative, or any other.
You can do these things with a stored procedure, or a user-defined function (but can't do DDL & DML in a function).
In detail, a stored procedure allows flow-of-control statements like IF THEN ELSE BEGIN END WHILE RETURN. You can use DML to UPDATE, DELETE and INSERT. You can use DDL to CREATE and DROP tables and indexes, add columns and constraints, and so on. You can return multiple rowsets. You can execute dynamic SQL.
What are you trying to accomplish?
Better way, you use a stored procedure instead of view.
There are you write multiple statement and also get output.
You can only have select statements in views. Therefore, 'truncate' is an invalid command to use.
TRUNCATE does not work with views.
Check out this link