Client 2 - Adding Error Checking

The client1.c application discussed has a fundamental flaw?there is no way to tell whether the connection attempt was successful. This next program attempts a connection and displays an error message if the attempt fails:


 1 /*

 2 ** File: client2.c

 3 */

 4

 5 #include <stdlib.h>

 6 #include <libpq-fe.h>

 7

 8 int main( int argc, char * argv[] )

 9 {

10   PGconn * connection;

11

12   if( argc != 2 )

13   {

14     printf( "usage  : %s \"connection-string\"\n", argv[0] );

15     printf( "example: %s \"user=myname password=cows\"\n", argv[0]);

16     exit( 1 );

17   }

18

19   if(( connection = PQconnectdb( argv[1] )) == NULL )

20   {

21     printf( "Fatal error - unable to allocate connection\n" );

22     exit( 1 );

23   }

24

25   if( PQstatus( connection ) != CONNECTION_OK )

26     printf( "%s\n", PQerrorMessage( connection ));

27   else

28     printf( "Connection ok, disconnecting\n" );

29

30   PQfinish( connection );

31

32   exit( 0 );

33

34 }

You can specify a connection string on the command line when you run this program. If you want to include more than one connection attribute, enclose the entire connection string in double quotes. For example:


$ ./client2 user=korry

Connection ok, disconnecting



$ ./client2 "user=korry password=cows"

Connection ok, disconnecting

I recommend that you run this program a few times, feeding it a variety of invalid connect strings so you become familiar with the error messages that you might receive when things go wrong. For example:


$ ./client2 host=badhost

connectDBStart() --  unknown hostname: badhost



$ ./client2 port=1000

connectDBStart() -- connect() failed: No such file or directory

        Is the postmaster running locally

        and accepting connections on Unix socket '/tmp/.s.PGSQL.1000'?



$ ./client2 badparameter

ERROR: Missing '=' after 'badparameter' in conninfo



$ ./client2 badparameter=1000

ERROR: Unknown conninfo option 'badparameter'

Viewing Connection Attributes

In the get_dflts application I showed you how to use the PQconndefaults() function to view the default connection attributes that will be used to establish a connection.

libpq also provides a number of functions that you can use to retrieve the actual connection attributes after you have a PGconn object. These functions are useful because in most situations, you won't explicitly specify every connection attribute. Instead, many (perhaps all) of the connection attributes will be defaulted for you.

PQconnectdb() will return a PGconn pointer in almost every case (only if libpq runs out of memory, PQconnectdb() will return a NULL pointer).

The following program attempts to make a connection and then print the set of connection parameters. I've modified client2.c to show the complete set of final connection parameters after a connection attempt. The new application is called client2b:


 1 /*

 2 ** File: client2b.c

 3 */

 4

 5 #include <stdlib.h>

 6 #include <libpq-fe.h>

 7

 8 static void show_connection_attributes( const PGconn * conn );

 9 static const char * check( const char * value );

10

11 int main( int argc, char * argv[] )

12 {

13   PGconn * connection;

14

15   if( argc != 2 )

16   {

17     printf( "usage  : %s \"connection-string\"\n", argv[0] );

18     printf( "example: %s \"user=myname password=cows\"\n", argv[0]);

19     exit( 1 );

20   }

21

22   if(( connection = PQconnectdb( argv[1] )) == NULL )

23   {

24     printf( "Fatal error - unable to allocate connection\n" );

25     exit( 1 );

26   }

27

28   if( PQstatus( connection ) != CONNECTION_OK )

29     printf( "%s\n", PQerrorMessage( connection ));

30   else

31     printf( "Connection ok\n" );

32

33   show_connection_attributes( connection );

34

35   PQfinish( connection );

36

37   exit( 0 );

38

39 }

40

41 static const char * check( const char * value )

42 {

43     if( value )

44     return( value );

45     else

46     return( "(null)" );

47 }

48

49 static void show_connection_attributes( const PGconn * c )

50 {

51   printf( "dbname   = %s\n", check( PQdb( c )));

52   printf( "user     = %s\n", check( PQuser( c )));

53   printf( "password = %s\n", check( PQpass( c )));

54   printf( "host     = %s\n", check( PQhost( c )));

55   printf( "port     = %s\n", check( PQport( c )));

56   printf( "tty      = %s\n", check( PQtty( c )));

57   printf( "options  = %s\n", check( PQoptions( c )));

58 }

Take a look at the show_connection_attributes() function (lines 49?58). Given a PGconn pointer, you can find the connection attributes that result after all the defaults are applied by calling PQdb(), PQuser(), and so on. In some cases, one of these functions returns a NULL pointer, so I wrapped each function invocation in a call to check() (lines 41?47) so you don't try to give any bad pointers to printf().

Remember that PQconnectdb() returns a PGconn pointer even when a connection attempt fails; it is often instructive to see the final connection attributes for a failed connection attempt. Here are the results when I try to connect to a nonexistent database on my system:


$ ./client2b user=korry

FATAL 1:  Database "korry" does not exist in the system catalog.



dbname   = korry

user     = korry

password =

host     = (null)

port     = 5432

tty      =

options  =

In this case, I can see that libpq chose an invalid database name (defaulted from my username).



    Part II: Programming with PostgreSQL