I defined a trigger in PostgreSQL 9.1 that should fire whenever a record is updated using:
CREATE OR REPLACE FUNCTION check() RETURNS TRIGGER AS $$
BEGIN
SELECT doubletEngine.checkForDoublet(NEW.id);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS check_upd on people;
CREATE TRIGGER check_upd AFTER UPDATE ON people
FOR EACH ROW
WHEN (OLD.* IS DISTINCT FROM NEW.*)
EXECUTE PROCEDURE check();
The method doubletEngine.checkForDoublet() was introduced to PL/Java 1.4.3 using
CREATE OR REPLACE FUNCTION doubletEngine.checkForDoublet(varchar) RETURNS void AS 'DoubletSearcher.checkForDoublet' LANGUAGE java;
Inside the method checkForDoublet(), I try to connect to the database using
Class.forName("org.postgresql.Driver");
String url = "jdbc:postgresql://127.0.0.1:5432/db";
c = DriverManager.getConnection(url, "postgres", "postgres");
if ( c == null ) {
throw new RuntimeException("Could not connect to server");
}
...but c is still null. Calling this code directly from my IDE works perfectly, but when the trigger fires, it only throws the RuntimeException. Is there anything I miss...???
According to the PL/Java documentation the connection should be obtained as follows:
Connection conn = DriverManager.getConnection("jdbc:default:connection");
Related
io.r2dbc.spi.Connection and PostgresqlConnection have createSavepoint, releaseSavepoint and rollbackTransactionToSavepoint methods.
How can I use these method by having R2dbcTransactionManager and TransactionalOperator?
I want to create an idempotent service which tries to insert to a table, if a unique constraint is violated then selects the existing record and continues with that
Mono.just(newOrder)
.flatMap(order -> orderRepository.save(order)
.onErrorResume(throwable -> orderRepository.findByUniqueField(newOrder.uniqueField))
.otherProcesses...
.as(transactionalOperator::transactional)
And I receive current transaction is aborted, commands ignored until end of transaction block from PostgreSQL
I saw this answer https://stackoverflow.com/a/48771320/5275087 but it seams autosave=always doesn't work with r2dbc
I wanted to try something like:
transactionalOperator.execute(reactiveTransaction -> {
GenericReactiveTransaction genericReactiveTransaction = (GenericReactiveTransaction) reactiveTransaction;
ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) genericReactiveTransaction.getTransaction();
Connection connection = txObject.getConnectionHolder().getConnection();
return Mono.from(connection.createSavepoint(TRANSACTION_LEVEL_1))
.then(Mono.just(newOrder)
.flatMap(order -> orderRepository.save(order)
.onErrorResume(throwable ->
Mono.from(connection.rollbackTransactionToSavepoint(TRANSACTION_LEVEL_1))
.then(orderRepository.findByUniqueField(newOrder.uniqueField)))
));
But R2dbcTransactionManager.ConnectionFactoryTransactionObject is a private class.
How do I achieve this without using reflection
I would like to safely drop Firebird table. I have 3 transactions, one to recreate table, one to do something with the table (just inserting a single row to keep it simple) and the last one to drop the table.
If all these txns are executed using single connection these works. If I use a different connection, then the drop command fails with
lock conflict on no wait transaction
unsuccessful metadata update
object TABLE "DEMO" is in use
private static void Test() {
using var conn1 = new FbConnection(ConnectionString);
using var conn2 = new FbConnection(ConnectionString);
using var conn3 = new FbConnection(ConnectionString);
conn1.Open();
conn2.Open();
conn3.Open();
ExecuteTxn(conn1, cmd => {
cmd.CommandText = "recreate table demo (id int primary key)";
cmd.ExecuteNonQuery();
});
ExecuteTxn(conn2, cmd => {
cmd.CommandText = "insert into demo (id) values (1)";
cmd.ExecuteNonQuery();
});
ExecuteTxn(conn3, cmd => {
cmd.CommandText = "drop table demo";
cmd.ExecuteNonQuery();
});
}
private static void ExecuteTxn(FbConnection conn, Action<FbCommand> todo) {
using (var txn = conn.BeginTransaction())
using (var cmd = conn.CreateCommand()) {
cmd.Transaction = txn;
todo(cmd);
txn.Commit();
}
}
I realized that changing the transaction options as
txn = conn.BeginTransaction(new FbTransactionOptions { TransactionBehavior = FbTransactionBehavior.Wait }))
seems to help. But I'm not sure if this the right thing to do or just a coincidence...
Using Firebird 3.0.6, FirebirdSql.Data.FirebirdClient.dll 7.5.0.0
As far as I understand it, the problem has to do with how Firebird caches certain metadata, which might result in existence locks being retained, which will prevent deletion of the object. In addition, it is possible - this is a guess! - that the Firebird ADO.net provider retains the statement handle with the insert statement prepared, which will also result in an existence lock being retained.
Executing in a WAIT transaction (optionally with a timeout) is considered an appropriate workaround by the Firebird core developers.
For reference, see the following tickets:
CORE-3766 - Transaction can`t change metadata if it is run in no_wait and there is another connect that once had queried these metadata
CORE-6382 - Triggers accessing a table prevent concurrent DDL command from dropping that table
In certain cases, switching from Firebird ClassicServer or Firebird SuperClassic to Firebird SuperServer can also prevent this problem.
However, if you want a more in-depth explanation, it might be worthwhile to ask this question on the firebird-devel mailing list.
What I am trying to do is to call a function whenever there is an update to the DB. I use the 'Committed transaction information' aspect to get the status on the DB update.
pg_xact_commit_timestamp(xid)
SELECT pg_xact_commit_timestamp(xmin) ts FROM "TABLE_NAME" WHERE pg_xact_commit_timestamp(xmin) IS NOT NULL;
The problem: After the first-iteration of update to the DB. The commit_timestamp(xmin) would never be empty. Is there a way that once the functions is executed we can set the commit_timestamp list to be empty?
My requirement: To execute a function only when there is an new (without any information on the previous update) update
I understand that, this is over use of an inbuilt-function.
E.g.
Connection DBconn = DBConnection.connect(url,user,password);
Boolean DBStatus = checkDBStatus(DBconn, configuration);
if(DBStatus) {
System.out.println("DB updated recently");
feedbackStatusJSON = runBusinessLogic();
}
else {
System.out.println("DB not updated");
}
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?
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
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));
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:
http://initd.org/psycopg/docs/advanced.html#asynchronous-notifications
https://gist.github.com/chernomyrdin/96812377f1ac5bf567b8
<?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);
}
}
}
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.
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
I have created a SOAPServerwithINDYVCL in Delphi XE. When a clients gets disconnected from server due to any reason like timeout at client side or net connection dropped.
I am getting error at server side as Socket Error 10053 - software caused connection abort.
this error popsup and blocking my thread of server.
I also checked this withe the demo of SOAPDMServerIndy. Here also if i start server, connect client and before server responds disconnect the client I get socket error.
Anyone knows how to handle this exception?
Even at this time this issue still exists. It's because of this code:
procedure TWebRequestHandler.HandleException(Sender: TObject);
var
Handled: Boolean;
Intf: IWebExceptionHandler;
begin
Handled := False;
if ExceptObject is Exception and
Supports(Sender, IWebExceptionHandler, Intf) then
try
Intf.HandleException(Exception(ExceptObject), Handled);
except
Handled := True;
System.SysUtils.ShowException(ExceptObject, ExceptAddr);
end;
if (not Handled) then
System.SysUtils.ShowException(ExceptObject, ExceptAddr);
end;
The line Intf.HandleException(Exception(ExceptObject), Handled) calls the TCustomWebDispatcher.HandleException procedure. This procedure checks for the existance of a OnException event handler in the TWebModule descendant in your application. If it exists (I would recommend it) the event handler procedure is called with the var argument 'Handled' set to true.
procedure TCustomWebDispatcher.HandleException(E: Exception; var Handled: Boolean);
begin
Handled := False;
if Assigned(FOnException) then
begin
Handled := True;
if Response <> nil then
Response.StatusCode := 500; { Allow the user to override the StatusCode }
FOnException(Self, E, Handled);
if Handled and (Response <> nil) then
Response.SendResponse;
end;
if not Handled then
if Response <> nil then
begin
Response.Content := Format(sInternalApplicationError, [E.Message, Request.PathInfo]);
if not Response.Sent then
begin
Response.StatusCode := 500;
Response.SendResponse;
end;
Handled := True;
end;
end;
When the handler returns there are two situations:
Handled is True; The response will be sent which leads to an new Exception. This one will be propagated back to the calling code (the procedure in the first block) so ShowException will be called.
Handled is False; Response <> nil will evaluate to True so when Response.Sent is False the same thing will happen.
I solved this by trying to send the response from the OnException event handler. When it fails an exception is raised. In that case I'm setting the Field 'FResponse' of the read only property 'Response' of my TWebModule's descendant to 'nil'. However, this field is a private field so I used some rtti to be able to access it.
procedure TwbmWebBrokerServerModule.UnlinkResponseObject;
begin
inherited;
TRTTIContext.Create.GetType(ClassInfo).GetField('FResponse').SetValue(Self, TValue.From(nil));
end;
procedure TwbmWebBrokerServerModule.WebModuleException(Sender: TObject; E: Exception; var Handled: Boolean);
begin
uVMI_Tools.AppendToLog(E.Message + ' (' + Request.PathInfo + ')');
if Request.Accept.Contains(CONTENTTYPE_APPLICATION_JSON) then
begin
Response.Content := StringToJSON(E.Message);
Response.ContentType := CONTENTTYPE_APPLICATION_JSON;
end
else
Response.Content := E.Message;
try
Response.SendResponse;
except
UnlinkResponseObject;
end;
end;
So when an exception occurs while sending the response, the property 'Response' is set to nil so there will be no attempt to send the response again.
There is no need to destroy the response object, assinging nil to it will suffice. It's because the Response object is created within TIdCustomHTTPServer class which just passed it to the TWebModule descendant. (Actually to the TWebModule's parent class TCustomWebDispatcher)