Client 4 - An Interactive Query Processor

To wrap up this chapter, you'll convert the interactive query processor from the previous chapter into a libpgeasy client.

Most of the code remains the same, so I'll point out only the differences. The most important change is that you no longer have to pass the PGconn * (connection handle) to every function?libpgeasy is managing the connection handle for you.


 1 /*

 2 ** File: client4.c

 3 */

 4

 5 #include <stdlib.h>

 6 #include <string.h>

 7 #include <libpq-fe.h>

 8 #include <libpgeasy.h>

 9 #include <readline/readline.h>

10 #include <readline/history.h>

11

12 typedef enum { FALSE, TRUE } bool;

13

14 #define MAX_PRINT_LEN40

15

16 static char separator[MAX_PRINT_LEN+1];

17

18 void print_result_set( PGresult * result )

19 {

20   int          col;

21   int          row;

22   int        * sizes;

23

24 /*

25 ** Compute the size for each column

26 */

27   sizes = (int *)calloc( PQnfields( result ), sizeof( int ));

28

29   for( col = 0; col < PQnfields( result ); col++ )

30   {

31     int   len = 0;

32

33     for( row = 0; row < PQntuples( result ); row++ )

34     {

35       if( PQgetisnull( result, row, col ))

36         len = 0;

37       else

38          len = PQgetlength( result, row, col );

39

40       if( len > sizes[col] )

41          sizes[col] = len;

42     }

43

44     if(( len = strlen( PQfname( result, col ))) > sizes[col] )

45       sizes[col] = len;

46

47     if( sizes[col] > MAX_PRINT_LEN )

48       sizes[col] = MAX_PRINT_LEN;

49   }

50

51 /*

52 ** Print the field names.

53 */

54   for( col = 0; col < PQnfields( result ); col++ )

55   {

56     printf( "%-*s ", sizes[col], PQfname( result, col ));

57   }

58

59   printf( "\n" );

60

61 /*

62 ** Print the separator line

63 */

64   memset( separator, '-', MAX_PRINT_LEN );

65

66   for( col = 0; col < PQnfields( result ); col++ )

67   {

68     printf( "%*.*s ", sizes[col], sizes[col], separator );

69   }

70

71   printf( "\n" );

72

73 /*

74 ** Now loop through each of the tuples returned by

75 ** our query and print the results.

76 */

77   for( row = 0; row < PQntuples( result ); row++ )

78   {

79     for( col = 0; col < PQnfields( result ); col++ )

80     {

81       if( PQgetisnull( result, row, col ))

82          printf( "%*s", sizes[col], "" );

83       else

84          printf( "%*s ", sizes[col], PQgetvalue(result, row, col));

85     }

86

87     printf( "\n" );

88

89   }

90   printf( "(%d rows)\n", PQntuples( result ));

91

92   free( sizes );

93 }

You can't use the fetch() or fetchwithnulls() in the print_result_set() function. There is no way to construct a call to these functions because you can't know (at the time the program is compiled) how many columns will be returned by a query.

The process_query() function is very simple. The call to doquery() sends the command to the server and returns a pointer to the result set.


 95 void process_query( char * buf )

 96 {

 97   PGresult *  result;

 98

 99   result = doquery( buf );

100

101   if( PQresultStatus( result ) == PGRES_TUPLES_OK )

102   {

103     print_result_set( result );

104   }

105   else if( PQresultStatus( result ) == PGRES_COMMAND_OK )

106   {

107     printf( "%s", PQcmdStatus( result ));

108

109     if( strlen( PQcmdTuples( result )))

110       printf( " - %s rows\n", PQcmdTuples( result ));

111     else

112       printf( "\n" );

113   }

114   else

115   {

116       printf( "%s\n", PQresultErrorMessage( result ));

117   }

118 }

The main() function is largely unchanged. I don't bother to save the connection handle returned by connectdb() because libpgeasy remembers it for me. The only other change in main() is that you set the error-handling mode calling on_error_continue(). If you don't set the error-handling mode, libpgeasy assumes that it should terminate your application if an error is encountered.


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

121 {

122   char    * buf;

123

124   connectdb( argc > 1 ? argv[1] : "" );

125

126   on_error_continue();

127

128   using_history();

129   read_history( ".pg_history" );

130

131   while(( buf = readline( "?>" )) != NULL )

132   {

133     if( strncmp( buf, "quit", sizeof( "quit" ) - 1  ) == 0 )

134     {

135       break;

136     }

137     else

138     {

139       if( strlen( buf ) != 0 )

140       {

141          add_history( buf );

142          process_query( buf );

143       }

144       free( buf );

145     }

146   }

147

148   write_history( ".pg_history" );

149

150   disconnectdb();

151

152   exit( EXIT_SUCCESS );

153 }



    Part II: Programming with PostgreSQL