Firebird "For Each Row" trigger syntax - firebird

There is confusion in the documentation and searching online about the support for statement and row level triggers. According to the documentation I've seen the latest version of Firebird supports both statement and row level triggers.
Firebird supposedly supports SQL-92/99. The standard approach for this is to use "for each row" in the trigger SQL, however, this causes an error in firebird.
Here is my statement level trigger, which works:
CREATE TRIGGER myExampleTrigger FOR myTable
AFTER UPDATE
AS
BEGIN
POST_EVENT 'testEvent';
END;
Here is my row level trigger, which doesn't work:
CREATE TRIGGER myExampleTrigger FOR myTable
AFTER UPDATE
AS
FOR EACH ROW
BEGIN
POST_EVENT 'testEvent';
END;
The statement-level trigger works to post an event for updates on myTable. When I update multiple rows it will only post one event.
What is the syntax for the trigger statement to get it to do a row-level trigger so that I can post an event FOR EACH ROW that is updated?

Firebird does not have statement-level triggers. Just create it as the first. It's a row-level trigger.
You said it posts only one event. It seems you also didn't understood how Firebird events works. It will be posted a single time, but you can see how many times by the event counter. They're are posted on commit.

Triggers in Firebird are always row level, never statement level. The documentation (Interbase 6.0 Language Reference, page 82; available from the Firebird website) says:
CREATE TRIGGER defines a new trigger to a database. A trigger is a self-contained
program associated with a table or view that automatically performs an action when a
row in the table or view is inserted, updated, or deleted.
As Adriano already explained, events are sent on transaction commit. If you post the same event multiple times in a single transaction, only a single event will be posted (with the count in the event).
Events are used to signal to other applications, not to the database itself (that is what triggers itself are for), so - afaik - you can't register for, nor determine the event count from within a trigger or stored procedure. The application registers for events. How this is done depends on the programming language and driver.
A lot of the (old) Interbase documentation shows example using EVENT INIT and EVENT WAIT, this however is only for embedded SQL which requires a preprocessor and is really hardly used. With Java and Jaybird you can use FBEventManager to listen for events, with C# and the Firebird .net provider you can use FbRemoteEvent. If you use the Firebird C API you need to use isc_que_events.

Related

A way to know if a Firebird table's data has changed without using a trigger

Is there a way of knowing that a table's data has changed (insert/update/delete) without using a trigger on that table? Perhaps a global trigger to indicate changes on a table?
If you want notification of changes, you will need to add a trigger yourself. Firebird 3 added a new feature to simplify identifying changed rows, the pseudo-column RDB$RECORD_VERSION. This pseudo-column contains the transaction that created the current version of a row.
Alternatively, you could try and use the trace facility to monitor for changes, but that is not an out of the box solution, as you will need to write the necessary logic to parse the trace output (and take things like transaction commit/rollback into account).

PostgreSQL - how to determine whether a transaction is active?

Let me open by saying: yes, I am aware of Determine if a transaction is active (Postgres)
Unfortunately the sole answer to that question is far too specific to the use case provided, and doesn't actually indicate whether or not a transaction is active.
The select txid_current(); trick suggested by How to check for pending operations in a PostgreSQL transaction doesn't appear to work - I always get the same transaction ID from adjacent calls to that function. Possibly this is because I'm trying to test it from pgAdmin, which is transparently starting transactions...? (Note: I don't actually care whether there are any pending changes or active locks, so looking at pg_locks isn't helpful - what if nothing's been touched since the transaction was started?)
So: How can I determine in PostgreSQL PL/pgSQL code if a transaction is currently active?
One possible use case is: the SP/FN in question will be doing its own explicit transaction management, and calling it with a transaction already active will greatly interfere with that. I want to raise an error so that the coding mistake of calling this SP/FN in a transaction can be corrected.
There are other use cases, though.
Ideally what I'm looking for is an equivalent to MSSQL's ##TRANCOUNT (though I don't really care how deeply the transactions may be nested...)
Postgres runs PL/pgSQL inside the transaction. Thus you can't control transaction from inside PL/pgSQL. Code will look like:
begin;
select plpgsql_fn();
do '/*same any plpgsql*/';
end;
So answering your question:
If you have PL/pgSQL running ATM, you have your transaction active ATM...
Of course you can do some trick, like starting/ending work over dblink or such. but then you can check select txid_current(); over the dblink successfully...
If you want to determine if there have been any data modifications in your transaction, call txid_current_if_assigned(). It returns NULL if nothing has been modified yet.
If you only want to know if you are inside some transaction, you can save yourself the trouble, because you always are.
Before PostgreSQL v11, you cannot use transaction control statements in a function.
I haven't found a clean way to do that, but you can always call BEGIN and if it succeeds it means there is no transaction in progress (don't forget to rollback). If it fails with "there is already a transaction in progress" this means you are within transaction (better not to rollback then).

when should I use an After trigger instead of a Before trigger?

Afaik, although I'm pretty new to Postgres, Before-triggers are less expensive then After-triggers.
After all, if you want to change the current record (using NEW), you can change the record before it is written. In contrast, with After-triggers you need two writes: 1 verbatim write and 1 as a result of the after-trigger.
At the same time, all functionality that is available in after-triggers seems to be available in before-triggers. If I'm not mistaken.
So why would you ever use After-triggers to begin with?
If you're changing the record upon which the trigger is acting use a BEFORE trigger. If you're doing some complex logic that may prevent the record from being changed, use a BEFORE trigger.
Almost anything else, use an AFTER trigger. An example might be where you're inserting child records which rely upon the primary key of a record being inserted. For example, if you're adding an entry to a history table for a newly inserted row. The parent row won't exist in the BEFORE trigger, so would fail foreign key checks.

Executing a trigger AFTER the completion of a transaction

In PostgreSQL, are DEFERRED triggers executed before (within) the completion of the transaction or just after it?
The documentation says:
DEFERRABLE
NOT DEFERRABLE
This controls whether the constraint can be deferred. A constraint
that is not deferrable will be checked immediately after every
command. Checking of constraints that are deferrable can be postponed
until the end of the transaction (using the SET CONSTRAINTS command).
It doesn't specify if it is still inside the transaction or out. My personal experience says that it is inside the transaction and I need it to be outside!
Are DEFERRED (or INITIALLY DEFERRED) triggers executed inside of the transaction? And if they are, how can I postpone their execution to the time when the transaction is completed?
To give you a hint what I'm after, I'm using pg_notify and RabbitMQ (PostgreSQL LISTEN Exchange) to send out messages. I process such messages in an external application. Right now I have a trigger which notifies the external app of the newly inserted records by including the record's id in the message. But in a non-deterministic way, once in a while, when I try to select a record by its id at hand, the record can not be found. That's because the transaction is not complete yet and the record is not actually added to the table. If I can only postpone the execution of the trigger for after the completion of the transaction, everything will work out.
In order to get better answers let me explain the situation even closer to the real world. The actual scenario is a little more complicated than what I explained before. The source code can be found here if anyone's interested. Becuase of reasons that I'm not gonna dig into, I have to send the notification from another database so the notification is actually sent like:
PERFORM * FROM dblink('hq','SELECT pg_notify(''' || channel || ''', ''' || payload || ''')');
Which I'm sure makes the whole situation much more complicated.
Triggers (including all sorts of deferred triggers) fire inside the transaction.
But that is not the problem here, because notifications are delivered between transactions anyway.
The manual on NOTIFY:
NOTIFY interacts with SQL transactions in some important ways.
Firstly, if a NOTIFY is executed inside a transaction, the notify
events are not delivered until and unless the transaction is
committed. This is appropriate, since if the transaction is aborted,
all the commands within it have had no effect, including NOTIFY. But
it can be disconcerting if one is expecting the notification events to
be delivered immediately. Secondly, if a listening session receives a
notification signal while it is within a transaction, the notification
event will not be delivered to its connected client until just after
the transaction is completed (either committed or aborted). Again, the
reasoning is that if a notification were delivered within a
transaction that was later aborted, one would want the notification to
be undone somehow — but the server cannot "take back" a notification
once it has sent it to the client. So notification events are only
delivered between transactions. The upshot of this is that
applications using NOTIFY for real-time signaling should try to keep
their transactions short.
Bold emphasis mine.
pg_notify() is just a convenient wrapper function for the SQL NOTIFY command.
If some rows cannot be found after a notification has been received, there must be a different cause! Go find it. Likely candidates:
Concurrent transactions interfering
Triggers doing something more or different than you think they do.
All sorts of programming errors.
Either way, like the manual suggests, keep transactions that send notifications short.
dblink
Update: Transaction control in a PROCEDURE or DO statement in Postgres 11 or later makes this a lot simpler. Just COMMIT; to (also) send waiting notifications.
Original answer (mostly for Postgres 10 or older):
PERFORM * FROM dblink('hq','SELECT pg_notify(''' || channel || ''', ''' || payload || ''')');
... which should be rewritten with format() to simplify and make the syntax secure:
PRERFORM dblink('hq', format('NOTIFY %I, %L', channel, payload));
dblink is a game-changer here, because it opens a separate transaction in the other database. This is sometimes used to fake autonomous transaction.
Does Postgres support nested or autonomous transactions?
How do I do large non-blocking updates in PostgreSQL?
dblink() waits for the remote command to finish. So the remote transaction will most probably commit first. The manual:
The function returns the row(s) produced by the query.
If you can send notification from the same transaction instead, that would be a clean solution.
Workaround for dblink
If notifications have to be sent from a different transaction, there is a workaround with dblink_send_query():
dblink_send_query sends a query to be executed asynchronously, that is, without immediately waiting for the result.
DO -- or plpgsql function
$$
BEGIN
-- do stuff
PERFORM dblink_connect ('hq', 'your_connstr_or_foreign_server_here');
PERFORM dblink_send_query('con1', format('SELECT pg_sleep(3); NOTIFY %I, %L ', 'Channel', 'payload'));
PERFORM dblink_disconnect('con1');
END
$$;
If you do this right before the end of the transaction, your local transaction gets 3 seconds (pg_sleep(3)) head start to commit. Chose an appropriate number of seconds.
There is an inherent uncertainty to this approach, since you get no error message if anything goes wrong. For a secure solution you need a different design. After successfully sending the command, chances for it to still fail are extremely slim, though. The chance that successful notifications are missed seem much higher, but that's built into your current solution already.
Safe alternative
A safer alternative would be to write to a queue table and poll it like discussed in #Bohemian's answer. This related answer demonstrates how to poll safely:
Postgres UPDATE … LIMIT 1
I'm posting this as an answer, assuming the actual problem you are trying to solve is deferring execution of an external process until after the transaction is completed (rather than the X-Y "problem" you're trying to solve using trigger Kung Fu).
Having the database tell an app to do something is a broken pattern. It's broken because:
There's no fallback if the app doesn't get the message, eg because it's down, network explodes, whatever. Even the app replying with an acknowledgment (which it can't), wouldn't fix this problem (see next point)
There's no sensible way to retry the work if the app gets the message but fails to complete it (for any of lots of reasons)
In contrast, using the database as a persistant queue, and having the app poll it for work, and take the work off the queue when work is complete, has none of the above problems.
There are lots of ways to achieve this. The one I prefer is to have some process (usually trigger on insert, update and delete) put data into a "queue" table. Have another process poll that table for work to do, and delete from the table when work is complete.
It also adds some other benefits:
The production and consumption of work is decoupled, which means you can safely kill and restart your app (which must happen from time to time, eg deploying) - the queue table will happily grow while the app is down, and will drain when the app is back up. You can even replace the app with an entirely new one
If for whatever reason you want to initiate processing of certain items, you can just manually insert rows into the queue table. I used this technique myself to initiate the processing of all items in a database that needed initialising by being put on the queue once. Importantly, I didn't need to do a perfunctory update to every row just to fire the trigger
Getting to your question, a slight delay can be introduced by adding a timestamp column to the queue table and having the poll query only select rows that are older than (say) 1 second, which gives the database time to complete its transaction
You can't overload the app. The app will read only as much work as it can handle. If your queue is growing, you need a faster app, or more apps If multiple consumers are operating, concurrency can be solved by (for example) adding a "token" column to the queue table
Queues that are backed by database tables is the basis of how persistent queues are implemented in commercial grade queue-based platforms, so the pattern is well tested, used and understood.
Leave the database to do what it does best, and the only thing it does well: Manage data. Don't try to make your database server into an app server.

How triggers works internally in SQL Server

Please correct me if I am wrong.
What I know about triggers is that they are triggered by events (Insert, Update, Delete). So we can run a stored procedure etc.. in the trigger.
This will give the application a good responsiveness because the query that the user interacts with, is quite small and this "other" longer time taking stuffs are taken care by the server internally as a separate task.
But I do not know about how the the triggers are handled inside the server. What I exactly want to know is what would happen in scenarios as given below.
Take Insert after trigger. And take trigger is executing a longer stored procedure. Then in the middle of the trigger there can be another insert. What I want to know is what will happen to that second trigger. If possible can I make that second trigger ignore itself.
marc_s has given the correct answer. I will copy it for the sake of completeness.
TRIGGERS ARE SYNCHRONOUS
If you want to have a asynchronous functionality go for a SQL broker implimenation.
Triggers are triggered by events - and then they are executed - right now. Since you cannot control when and how often they are triggered, you should keep the processing in those triggers to an absolute minimum - I always try to make - at most - an entry into another table (an "Audit" table) or possibly put a "marker" row into a "command" table. But the actual processing of that info - running stored procedures etc. - should be left to an outside job - don't do extensive processing in a trigger! This will reliably KILL all your performance\responsiveness.