Postgres trigger notify with PHP
Asked Answered
A

5

-1

I have a problem or misunderstanding with Postgre trigger -> perform notify -> capture into PHP flow.

My Platform is PHP(5.6) in centos with Postgres.

I have to add trigger with notifications table and whenever a new notification is added to that notifications SMS has to send to that user.

So here added trigger like this

CREATE FUNCTION xxx_sms_trigger() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
DECLARE
BEGIN
  PERFORM pg_notify('sms', NEW.id||'' );
  RETURN new;
END;

and in php the inserting new notifications work fine.

Now I have a separate file where added this capturing pg_notify triggering by "pg_get_notify", here I couldn't get this flow totally like how Postgres can trigger some unknown php script without its being running as service or how I can make it work?

Arvind answered 9/5, 2014 at 18:49 Comment(5)
What is "Postgre" supposed to be? The older brother of "My", maybe? And please use a title that makes sense.Allurement
"Its not right combination" says who? Most people don't realize that mySQL is a pit of horrors until they're in too deep to escape.Trinitrotoluene
@Erwin if you dont understand please see below answered people how they understood.Arvind
@Trinitrotoluene I didnt came across any single application that is running on php with postgres and finding hard at times like this sort trigger usage with PHP. Secondly this was running smoothly with Node JS which was seems right combination.Arvind
@Ela you didn't look hard enough. trigger signaling was a concept even before node.js was invented.Linear
T
3

You do need a php script running as a service. If that is going to be the language that receives the notification you provide. As @FelipeRosa says, that script will need to connect to the database, then issue at least one command:

listen sms;

There is a good example of the listen on the main site (http://www.php.net/manual/en/function.pg-get-notify.php)

I haven't coded in php in a few years. Recently I have implemented this logic in python, but it should be about the same. I did a little research, and I can find select() in php, but it seems that the postgres socket descriptor is not available in php, so you can't use the select() in php unless you can find the postgres socket descriptor.

Anyway, that thread is here (http://postgresql.1045698.n5.nabble.com/Is-there-any-way-to-listen-to-NOTIFY-in-php-without-polling-td5749888.html). There is a polling example in there for your php script side down near the bottom. You can do the listen as previous selected (once), then put your pg_get_notify() in a loop with a sleep in there for the amount of time you are willing to queue notifications.

Just fwiw, in python I don't poll, I select.select(pg_conn,...), when data arrives on the postgres connection I check it for notifications, so there is no 'polling'. It would be nice if you could find a way to use select() in php instead of looping.

-g

Tithe answered 9/5, 2014 at 20:9 Comment(0)
I
3

Here’s a cohesive example that registers an interest in a table insertion, waits for notification (or timeout) and responds to the caller. We use a timestamp preceded by the letter ‘C’ to identify the notification channel since Postgres requires the channel name to be a proper identifier.

Postgres SQL

/*  We want to know when items of interest get added to this table.   
    Asynchronous insertions possible from different process or server  */
DROP TABLE IF EXISTS History;
CREATE TABLE History (
   HistoryId INT PRIMARY KEY,
   MYKEY CHAR(17),
   Description TEXT,
   TimeStamp BIGINT
);

/*  Table of registered interest in a notification  */
DROP TABLE IF EXISTS Notifications;
CREATE TABLE Notifications (
   NotificationId INT PRIMARY KEY,
   Channel VARCHAR(20),
   MYKEY CHAR(17)
);

/*  Function to process a single insertion to History table  */
CREATE OR REPLACE FUNCTION notify_me()
  RETURNS trigger AS
$BODY$
  DECLARE ch varchar(20);
  BEGIN
     FOR ch IN
     SELECT DISTINCT Channel FROM Notifications 
        WHERE MYKEY=NEW.MYKEY
     LOOP
        /* NOTIFY ch, 'from notify_me trigger'; */
        EXECUTE 'NOTIFY C' || ch || ', ' || quote_literal('from notify_me') || ';';
        DELETE FROM Notifications WHERE Channel=ch;
     END LOOP;
     RETURN NULL;
  END;
$BODY$
LANGUAGE 'plpgsql';

/*  Trigger to process all insertions to History table  */
DROP TRIGGER IF EXISTS HistNotify ON History CASCADE;
CREATE TRIGGER HistNotify AFTER INSERT ON History
    FOR EACH ROW EXECUTE PROCEDURE notify_me();

PHP code

// $conn is a PDO connection handle to the Postgres DB
// $MYKEY is a key field of interest
$TimeStamp = time();  //  UNIX time (seconds since 1970) of the request
$timeout = 120;  //  Maximum seconds before responding

//  Register our interest in new history log activity
$rg = $conn->prepare("INSERT INTO Notifications (MYKEY, Channel)  VALUES (?,?)");
$rg->execute(array($MYKEY, $TimeStamp));

//  Wait until something to report
$conn->exec('LISTEN C'.$TimeStamp.';');  //  Prepend ‘C’ to get notification channel
$conn->exec('COMMIT;');  //  Postgres may need this to start listening
$conn->pgsqlGetNotify (PDO::FETCH_ASSOC, $timeout*1000);  //  Convert  from sec to ms

//  Unregister our interest
$st = $conn->prepare("DELETE FROM Notifications WHERE Channel=?");  
$st->execute(array($TimeStamp));
Inhabited answered 19/11, 2015 at 22:55 Comment(0)
M
3

Here is an example how to migrate the "Python way" mentioned by @Greg to PHP. After starting the script below - open a new connection to the postgres db and query NOTIFY "test", 'I am the payload'

Sources:


<?php

$dsn = 'user=postgres dbname=postgres password=postgres port=5432 host=localhost';

$connection = \pg_connect($dsn);

if (\pg_connection_status($connection) === \PGSQL_CONNECTION_BAD) {
    throw new \Exception(
        sprintf('The database connect failed: %s', \pg_last_error($connection))
    );
}

\pg_query('LISTEN "test"');

while (true) {
    $read = [\pg_socket($connection)];
    $write = null;
    $except = null;
    $num = \stream_select(
        $read,
        $write,
        $except,
        60
    );
    if ($num === false) {
        throw new \Exception('Error in optaining the stream resource');
    }
    if (\pg_connection_status($connection) !== \PGSQL_CONNECTION_OK) {
        throw new \Exception('pg_connection_status() is not PGSQL_CONNECTION_OK');
    } elseif ($num) {
        $notify = \pg_get_notify($connection);
        if ($notify !== false) {
            var_dump($notify);
        }
    }
}
Merwyn answered 17/12, 2017 at 14:21 Comment(0)
A
1

Its a litte example:

The PHP script (I named it teste.php - It's the same at http://php.net/manual/pt_BR/function.pg-get-notify.php):

$conn = pg_pconnect("dbname=mydb");
if (!$conn) {
  echo "An error occurred.\n";
  exit;
}

while(true){
   pg_query($conn, 'LISTEN SMS;');
   $notify = pg_get_notify($conn);
     if (!$notify) {
       echo "No messages\n";
       // change it as u want
     } else {
       print_r($notify);
       //your code here
     }
     sleep(2);
}

Keep the script runnig (I assumed u are using linux):

php teste.php > log.txt 2>&1 &

Note that:

2>&1 redirects both standard output and standard error into the log.txt file.

& runs the whole thing in the background

You can follow the log.txt with this command:

tail -f log.txt
Addend answered 5/4, 2017 at 13:40 Comment(0)
H
0

According to this you should first make the application listen to the desired channel issuing the command "LISTEN ", via pg_query for example, before you can notify messages to the application.

Hofer answered 9/5, 2014 at 19:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.