Client 4 ”An Interactive Query ProcessorTo 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 } |