diff --git a/doc/src/sgml/libpgeasy.sgml b/doc/src/sgml/libpgeasy.sgml index 9196621c8c..4bb1b7949b 100644 --- a/doc/src/sgml/libpgeasy.sgml +++ b/doc/src/sgml/libpgeasy.sgml @@ -1,5 +1,5 @@ @@ -11,7 +11,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/Attic/libpgeasy.sgml,v 2.8 2002/01/07 02:29 Written by Bruce Momjian (pgman@candle.pha.pa.us) - and last updated 2000-03-30 + and last updated 2002-03-04 @@ -19,13 +19,12 @@ $Header: /cvsroot/pgsql/doc/src/sgml/Attic/libpgeasy.sgml,v 2.8 2002/01/07 02:29 pgeasy allows you to cleanly interface to the libpq library, more like a 4GL SQL interface. Refer to for more - information about libpq + information about libpq. - It consists of set of simplified C functions that encapsulate the - functionality of libpq. - The functions are: + It consists of a set of simplified C functions that encapsulate the + functionality of libpq. The functions are: @@ -88,53 +87,50 @@ void set_result(PGresult *newres); - - -void unset_result(PGresult *oldres); - - - Many functions return a structure or value, so you can do more work + Many functions return a structure or value, so you can work with the result if required. - You basically connect to the database with connectdb, - issue your query with doquery, - fetch the results with fetch, - and finish with disconnectdb. + You basically connect to the database with + connectdb, issue your query with + doquery, fetch the results with + fetch, and finish with + disconnectdb. For SELECT queries, fetch - allows you to pass pointers as parameters, and on return the variables - are filled with data from the binary cursor you opened. These binary - cursors cannot be used if you are running the - pgeasy - client on a system with a different architecture than the database - server. If you pass a NULL pointer parameter, the column is skipped. - fetchwithnulls allows you to retrieve the NULL - status of the field by passing an int* - after each result pointer, which returns true or false if the field is null. - You can always use libpq functions on the PGresult pointer returned - by doquery. - reset_fetch starts the fetch back at the beginning. + allows you to pass pointers as parameters, and on return the + variables are filled with data from the binary cursor you opened. + These binary cursors cannot be used if you are running the + pgeasy client on a system with a different + architecture than the database server. If you pass a NULL pointer + parameter, the column is skipped. fetchwithnulls + allows you to retrieve the NULL status of the field by passing an + int* after each result pointer, which returns true + or false to indicate if the field is null. You can always use + libpq functions on the + PGresult pointer returned by + doquery. reset_fetch starts + the fetch back at the beginning. - get_result, - set_result, - and - unset_result - allow you to handle multiple result sets at the same time. + get_result and set_result + allow you to handle multiple open result sets. Use + get_result to save a result into an application + variable. You can then later use set_result to + return to the previously save result. - There are several demonstration programs in the - source directory. + There are several demonstration programs in + pgsql/src/interfaces/libpgeasy/examples. diff --git a/src/interfaces/libpgeasy/examples/Makefile b/src/interfaces/libpgeasy/examples/Makefile index 1885bb425f..af8dc0c32a 100644 --- a/src/interfaces/libpgeasy/examples/Makefile +++ b/src/interfaces/libpgeasy/examples/Makefile @@ -4,23 +4,17 @@ # Makefile for pgeasy examples # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/interfaces/libpgeasy/examples/Attic/Makefile,v 1.2 2000/05/18 14:24:37 momjian Exp $ +# $Header: /cvsroot/pgsql/src/interfaces/libpgeasy/examples/Attic/Makefile,v 1.3 2002/03/04 18:50:21 momjian Exp $ # #------------------------------------------------------------------------- CFLAGS=-I/usr/local/pgsql/include -TARGET = pginsert pgwordcount pgnulltest +TARGET = pginsert pgwordcount pgnulltest pgmultiresult LDFLAGS = -L/usr/local/pgsql/lib -lpgeasy all : $(TARGET) -pginsert: - gcc -o $@ $(CFLAGS) $@.c $(PGEASY) $(LDFLAGS) - -pgwordcount: - gcc -o $@ $(CFLAGS) $@.c $(PGEASY) $(LDFLAGS) - -pgnulltest: +%: %.c gcc -o $@ $(CFLAGS) $@.c $(PGEASY) $(LDFLAGS) clean: diff --git a/src/interfaces/libpgeasy/libpgeasy.c b/src/interfaces/libpgeasy/libpgeasy.c index 4bddece659..40684a2cfe 100644 --- a/src/interfaces/libpgeasy/libpgeasy.c +++ b/src/interfaces/libpgeasy/libpgeasy.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "libpq-fe.h" #include "halt.h" @@ -16,31 +17,33 @@ #endif #ifndef TRUE -#define TRUE 1 +#define TRUE 1 #endif #ifndef FALSE -#define FALSE 0 +#define FALSE 0 #endif /* GLOBAL VARIABLES */ static PGconn *conn; static PGresult *res = NULL; -static int tuple; /* stores fetch location */ +static int tuple; /* stores fetch location */ -#define ON_ERROR_STOP 0 -#define ON_ERROR_CONTINUE 1 +#define ON_ERROR_STOP 0 +#define ON_ERROR_CONTINUE 1 -static int on_error_state = ON_ERROR_STOP; /* halt on errors? */ +static int on_error_state = ON_ERROR_STOP; /* halt on errors? */ + +static int user_has_res = FALSE; + +static void add_res_tuple(void); +static void get_res_tuple(void); +static void del_res_tuple(void); -static int in_result_block = FALSE; -static int was_get_unset_result = FALSE; /* - * * connectdb - returns PGconn structure - * */ PGconn * connectdb(char *options) @@ -53,17 +56,14 @@ connectdb(char *options) return conn; } + /* - * * disconnectdb - * */ void disconnectdb() { - if (res != NULL && - in_result_block == FALSE && - was_get_unset_result == FALSE) + if (res != NULL && user_has_res == FALSE) { PQclear(res); res = NULL; @@ -72,20 +72,17 @@ disconnectdb() PQfinish(conn); } + /* - * * doquery - returns PGresult structure - * */ PGresult * doquery(char *query) { - if (res != NULL && - in_result_block == FALSE && - was_get_unset_result == FALSE) + if (res != NULL && user_has_res == FALSE) PQclear(res); - was_get_unset_result = FALSE; + user_has_res = FALSE; res = PQexec(conn, query); if (on_error_state == ON_ERROR_STOP && @@ -105,11 +102,10 @@ doquery(char *query) return res; } + /* - * * fetch - returns tuple number (starts at 0), or the value END_OF_TUPLES * NULL pointers are skipped - * */ int fetch(void *param,...) @@ -142,8 +138,8 @@ fetch(void *param,...) return tuple++; } + /* - * * fetchwithnulls - returns tuple number (starts at 0), * or the value END_OF_TUPLES * Returns TRUE or FALSE into null indicator variables @@ -185,10 +181,19 @@ fetchwithnulls(void *param,...) return tuple++; } + +/* + * reset_fetch + */ +void +reset_fetch() +{ + tuple = 0; +} + + /* - * * on_error_stop - * */ void on_error_stop() @@ -196,10 +201,9 @@ on_error_stop() on_error_state = ON_ERROR_STOP; } + /* - * * on_error_continue - * */ void on_error_continue() @@ -209,92 +213,132 @@ on_error_continue() /* - * * get_result - * */ PGresult * get_result() { - char *cmdstatus = PQcmdStatus(res); + if (res == NULL) + halt("get_result called with no result pointer used\n"); - was_get_unset_result = TRUE; + /* delete it if it is already there; we are about to re-add it */ + del_res_tuple(); - /* we have to store the fetch location somewhere */ - /* XXX THIS IS A NO-NO */ - cmdstatus[0] = NUL; - memcpy(&cmdstatus[1], &tuple, sizeof(tuple)); + /* we have to store the fetch location */ + add_res_tuple(); + + user_has_res = TRUE; return res; } + /* - * * set_result - * */ void set_result(PGresult *newres) { - - char *cmdstatus = PQcmdStatus(res); - if (newres == NULL) halt("set_result called with null result pointer\n"); - if (res != NULL && was_get_unset_result == FALSE) + if (res != NULL && user_has_res == FALSE) { - if (in_result_block == FALSE) - PQclear(res); - else - { - /* XXX THIS IS A NO-NO */ - cmdstatus[0] = NUL; - memcpy(&cmdstatus[1], &tuple, sizeof(tuple)); - } + /* + * Basically, throw away res. We can't return to it because the + * user doesn't have the res pointer. + */ + del_res_tuple(); + PQclear(res); } - in_result_block = TRUE; - was_get_unset_result = FALSE; - - cmdstatus = PQcmdStatus(newres); - memcpy(&tuple, &cmdstatus[1], sizeof(tuple)); + user_has_res = FALSE; res = newres; + + get_res_tuple(); } /* - * - * unset_result - * + * Routines to store res/tuple mapping + * This is used to keep track of fetch locations while using get/set on + * result sets. + * Auto-growing array is used, with empty slots marked by res == NULL */ -void -unset_result(PGresult *oldres) + +static struct res_tuple { - char *cmdstatus = PQcmdStatus(oldres); + PGresult *res; + int tuple; +} *res_tuple = NULL; - if (oldres == NULL) - halt("unset_result called with null result pointer\n"); +static int res_tuple_len = 0; - if (in_result_block == FALSE) - halt("Unset of result without being set.\n"); - - was_get_unset_result = TRUE; - - /* XXX THIS IS A NO-NO */ - cmdstatus[0] = NUL; - memcpy(&cmdstatus[1], &tuple, sizeof(tuple)); - in_result_block = FALSE; -} /* - * - * reset_fetch - * + * add_res_tuple */ -void -reset_fetch() +static void +add_res_tuple(void) { - tuple = 0; + int i, + new_res_tuple_len = res_tuple_len ? res_tuple_len * 2 : 1; + + for (i = 0; i < res_tuple_len; i++) + /* Put it in an empty slot */ + if (res_tuple[i].res == NULL) + { + res_tuple[i].res = res; + res_tuple[i].tuple = tuple; + } + + /* Need to grow array */ + res_tuple = realloc(res_tuple, new_res_tuple_len * sizeof(struct res_tuple)); + + /* clear new elements */ + for (i = res_tuple_len; i < new_res_tuple_len; i++) + { + res_tuple[i].res = NULL; + res_tuple[i].tuple = 0; + } + + /* recursion to add entry */ + add_res_tuple(); +} + + +/* + * get_res_tuple + */ +static void +get_res_tuple(void) +{ + int i; + + for (i = 0; i < res_tuple_len; i++) + if (res_tuple[i].res == res) + { + tuple = res_tuple[i].tuple; + return; + } + halt("get_res_tuple called with invalid result pointer\n"); +} + + +/* + * del_res_tuple + */ +static void +del_res_tuple(void) +{ + int i; + + for (i = 0; i < res_tuple_len; i++) + if (res_tuple[i].res == res) + { + res_tuple[i].res = NULL; + return; + } + return; }