]> granicus.if.org Git - postgresql/commitdiff
Improve libpgeasy API for multiple result sets, add example.
authorBruce Momjian <bruce@momjian.us>
Mon, 4 Mar 2002 18:50:21 +0000 (18:50 +0000)
committerBruce Momjian <bruce@momjian.us>
Mon, 4 Mar 2002 18:50:21 +0000 (18:50 +0000)
doc/src/sgml/libpgeasy.sgml
src/interfaces/libpgeasy/examples/Makefile
src/interfaces/libpgeasy/libpgeasy.c

index 9196621c8cc0dc63971516815b328dfe5123a1c9..4bb1b7949bd22a75afbdd2850795d78a70db3ac8 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/Attic/libpgeasy.sgml,v 2.8 2002/01/07 02:29:12 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/Attic/libpgeasy.sgml,v 2.9 2002/03/04 18:50:20 momjian Exp $
 -->
 
  <chapter id="pgeasy">
@@ -11,7 +11,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/Attic/libpgeasy.sgml,v 2.8 2002/01/07 02:29
    <para>
     Written by Bruce Momjian
     (<email>pgman@candle.pha.pa.us</email>)
-    and last updated 2000-03-30
+    and last updated 2002-03-04
    </para>
   </note>
 
@@ -19,13 +19,12 @@ $Header: /cvsroot/pgsql/doc/src/sgml/Attic/libpgeasy.sgml,v 2.8 2002/01/07 02:29
    <application>pgeasy</application> allows you to cleanly interface
    to the <application>libpq</application> library, more like a 4GL
    SQL interface.  Refer to <xref linkend="libpq"> for more
-   information about <application>libpq</application>
+   information about <application>libpq</application>.
   </para>
 
   <para>
-   It consists of set of simplified C functions that encapsulate the
-   functionality of <application>libpq</application>.
-   The functions are:
+   It consists of a set of simplified C functions that encapsulate the
+   functionality of <application>libpq</application>. The functions are:
 
    <itemizedlist>
     <listitem>
@@ -88,53 +87,50 @@ void        set_result(PGresult *newres);
 </synopsis>
     </listitem>
 
-    <listitem>
-<synopsis>
-void        unset_result(PGresult *oldres);
-</synopsis>
-    </listitem>
    </itemizedlist>
   </para>
 
   <para>
-   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.
   </para>
 
   <para>
-   You basically connect to the database with <function>connectdb</function>,
-   issue your query with <function>doquery</function>,
-   fetch the results with <function>fetch</function>,
-   and finish with <function>disconnectdb</function>.
+   You basically connect to the database with
+   <function>connectdb</function>, issue your query with
+   <function>doquery</function>, fetch the results with
+   <function>fetch</function>, and finish with
+   <function>disconnectdb</function>.
   </para>
 
   <para>
    For <literal>SELECT</literal> queries, <function>fetch</function>
-   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
-   <application>pgeasy</application>
-   client on a system with a different architecture than the database
-   server.  If you pass a NULL pointer parameter, the column is skipped.
-   <function>fetchwithnulls</function> allows you to retrieve the NULL
-   status of the field by passing an <literal>int*</literal>
-   after each result pointer, which returns true or false if the field is null.
-   You can always use <application>libpq</application> functions on the <structname>PGresult</structname> pointer returned
-   by <function>doquery</function>.
-   <function>reset_fetch</function> 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
+   <application>pgeasy</application> client on a system with a different
+   architecture than the database server. If you pass a NULL pointer
+   parameter, the column is skipped. <function>fetchwithnulls</function>
+   allows you to retrieve the NULL status of the field by passing an
+   <literal>int*</literal> after each result pointer, which returns true
+   or false to indicate if the field is null. You can always use
+   <application>libpq</application> functions on the
+   <structname>PGresult</structname> pointer returned by
+   <function>doquery</function>. <function>reset_fetch</function> starts
+   the fetch back at the beginning.
   </para>
 
   <para>
-   <function>get_result</function>,
-   <function>set_result</function>,
-   and
-   <function>unset_result</function>
-   allow you to handle multiple result sets at the same time.
+   <function>get_result</function> and <function>set_result</function>
+   allow you to handle multiple open result sets. Use
+   <function>get_result</function> to save a result into an application
+   variable. You can then later use <function>set_result</function> to
+   return to the previously save result.
   </para>
 
   <para>
-   There are several demonstration programs in the
-   source directory.
+   There are several demonstration programs in
+   <filename>pgsql/src/interfaces/libpgeasy/examples</>.
   </para>
  </chapter>
 
index 1885bb425fba7c79d723b2f5c9c7bea12667cf39..af8dc0c32a77a0ff3d15bdf23aec66e27d03035d 100644 (file)
@@ -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:
index 4bddece659cb3d85673beda001ce79c29e709f82..40684a2cfe8205a726bd3b31a0e1a843ec192bc2 100644 (file)
@@ -6,6 +6,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdarg.h>
+#include <stdlib.h>
 
 #include "libpq-fe.h"
 #include "halt.h"
 #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;
+/*
+ * add_res_tuple
+ */
+static void
+add_res_tuple(void)
+{
+       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;
+       }
 
-       /* XXX THIS IS A NO-NO */
-       cmdstatus[0] = NUL;
-       memcpy(&cmdstatus[1], &tuple, sizeof(tuple));
-       in_result_block = FALSE;
+       /* recursion to add entry */
+       add_res_tuple();
 }
 
+
 /*
- *
- *     reset_fetch
- *
+ * get_res_tuple
  */
-void
-reset_fetch()
+static void
+get_res_tuple(void)
 {
-       tuple = 0;
+       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;
 }