I'm enhancing a ROS (robot operating system) stack sql_database to be able to handle postgresqls LISTEN and NOTIFY commands. As mentioned, I'm using libpq version 9.1.10-0 on Ubuntu12.04 within a C++ program. But for some reasons I'm not able to retrieve a NOTIFY.
I know, that there's an example (Example 28-2. libpq Example Program 2) and it works perfectly fine. I've played around with it quite a long time and also tried to copy it to my code as exactly as possible and change the example code in a way making it more similar to the code I'm having problems with. But that didn't help for my problem.
I can received notifies in the example program and in a manual login into the database, but not in the code I want to use.
What else did I try:
- Connecting to a different database - didn't change anything.
- Doing a
COMMIT;
after I performed aLISTEN <channel>;
command. But this caused the warning as expected as I didn't have an open transaction. - Check if the connection is dead before I do
PQconsumeInput(connection_);
- it's purely alive - perform the LISTEN command in the same function, where I'm checking for a NOTIFY (using a breakpoint to trigger a NOTIFY in between) - didn't change anything.
The NOTIFY was always triggered manually with NOTIFY <channel>;
Codes structure
The code can also be seen here on github (on the unstable branches):
class PostgresqlDatabase (in sql_interface->database_interface->src on github)
This class holds the connection PGconn and provides tasks likebool listenToChannel(std::string channel);
The main purpose of that class is to abstract the sql queries, so that ROS-programmers don't have to care about them anymore.
class databaseBinding
it's the glue between ROS and the database functionalities. It holds a PostgresqlDatabase object to get a database connection and to call the tasks.A main function
Does the following things- do some ROS initialization stuff
- create a databaseBinding object, which will initializes a PostgresqlDatabase object, which builds up a connection to my database
- call the
PostgresqlDatabase::listenToChannel(std::string channel)
-function - Go in a loop checking for a NOTIFY periodically using the
PostgresqlDatabase::checkNotify(notification &no)
-function
Some code
Checking for a NOTIFY
The checkNotify function, which is triggered about 5 times per second:
/*! Checks for a received NOTIFY and returns it. */
bool PostgresqlDatabase::checkNotify(notification &no)
{
PGnotify *notify;
PQconsumeInput(connection_);
if ((notify = PQnotifies(connection_)) != NULL)
{
no.channel = notify->relname;
no.sending_pid = notify->be_pid;
no.payload = notify->extra;
PQfreemem(notify);
return true;
} else
{
no.channel = "";
no.sending_pid = 0;
no.payload = "";
PQfreemem(notify);
return false;
}
}
Listening to a channel
/*! Listens to a specified channel using the Postgresql LISTEN-function.*/
bool PostgresqlDatabase::listenToChannel(std::string channel) {
//look, if we're already listening to the channel in our list
if (std::find(channels_.begin(),channels_.end(),channel) == channels_.end() )
{
std::string query = "LISTEN " + channel;
PGresultAutoPtr result = PQexec(connection_,query.c_str());
if (PQresultStatus(*result) != PGRES_COMMAND_OK)
{
ROS_WARN("LISTEN command failed: %s", PQerrorMessage(connection_));
return false;
}
ROS_INFO("Now listening to channel \"%s\"",channel.c_str());
channels_.push_back(channel);
return true;
}
ROS_INFO("We are already listening to channel \"%s\" - nothing to be done",channel.c_str());
return true;
}
So it turned out, that there was something wrong with the connection. It was created with that code:
With
host=192.168.10.100, port=5432, user=turtlebot, password= , dbname=rosdb.
But an empty username doesn't satisfy the usage of PQconnectdb, which, for some parsing-reason, caused it to login to the database "turtlebot". That database unfortunately existed on my server. And there it - of course - didn't get any notifications sent out in the "rosdb"-database and had a good connection.
What an, for me, awkward and unlucky behaviour.