--- /dev/null
+#
+# Makefile
+#
+#
+PGINTERFACE = pginterface.o halt.o
+TARGET = pginsert pgwordcount pgnulltest
+CFLAGS = -g -Wall -I/u/postgres95/include
+LDFLAGS = -L/u/postgres95/lib -lpq
+
+all : $(TARGET)
+
+$(TARGET): $(PGINTERFACE) $*.c
+ cc -o $* $(CFLAGS) $*.c $(PGINTERFACE) $(LDFLAGS)
+
+$(PGINTERFACE): pginterface.c halt.c
+ cc -c $(CFLAGS) pginterface.c halt.c
+
+clean:
+ rm -f *.o $(TARGET) log core
+
+install:
+ make clean
+ make CFLAGS=-O
+ install -s -o bin -g bin $(TARGET) /usr/local/bin
--- /dev/null
+
+
+ Pginterface 1.0
+
+Attached is a copy of the Postgres support routines I wrote to allow me
+to more cleanly interface to the libpg library, more like a 4gl SQL
+interface.
+
+It has several features that may be useful for others:
+
+I have simplified the C code that calls libpq by wrapping all the
+functionality of libpq in calls to connectdb(), doquery(), fetch(),
+fetchisnull() and disconnectdb(). Each call returns a structure or
+value, so if you need to do more work with the result, you can. Also, I
+have a global variable that allows you to disable the error checking I
+have added to the doquery() routine.
+
+I have added a function called fetch(), which allows you to pass
+pointers as parameters, and on return the variables are filled with the
+data from the binary cursor you opened. These binary cursors are not
+useful if you are running the query engine on a system with a different
+architecture than the database server. If you pass a NULL pointer, the
+column is skipped, and you can use libpq to handle it as you wish.
+
+I have used sigprocmask() to block the reception of certain signals
+while the program is executing SQL queries. This prevents a user
+pressing Control-C from stopping all the back ends. It blocks SIGHUP,
+SIGINT, and SIGTERM, but does not block SIGQUIT or obviously kill -9.
+If your platform does not support sigprocmask(), you can remove those
+function calls. ( Am I correct that abnormal termination can cause
+shared memory resynchronization?)
+
+There is a demo program called pginsert that demonstrates how the
+library can be used.
+
+You can create a library of pginterface.c and halt.c, and just include
+pginterface.h in your source code.
+
+I am willing to maintain this if people find problems or want additional
+functionality.
+
+Bruce Momjian (root@candle.pha.pa.us)
--- /dev/null
+/*
+**
+** halt.c
+**
+** This is used to print out error messages and exit
+*/
+
+#include <varargs.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+
+/*-------------------------------------------------------------------------
+**
+** halt - print error message, and call clean up routine or exit
+**
+**------------------------------------------------------------------------*/
+
+/*VARARGS*/
+void halt(va_alist)
+va_dcl
+{
+ va_list arg_ptr;
+ char *format, *pstr;
+ void (*sig_func)();
+
+ va_start(arg_ptr);
+ format = va_arg(arg_ptr,char *);
+ if (strncmp(format,"PERROR", 6) != 0)
+ vfprintf(stderr,format,arg_ptr);
+ else
+ {
+ for (pstr=format+6; *pstr == ' ' || *pstr == ':'; pstr++)
+ ;
+ vfprintf(stderr,pstr,arg_ptr);
+ perror("");
+ }
+ va_end(arg_ptr);
+ fflush(stderr);
+
+ /* call one clean up function if defined */
+ if ( (sig_func = signal(SIGTERM, SIG_DFL)) != SIG_DFL &&
+ sig_func != SIG_IGN)
+ (*sig_func)(0);
+ else if ( (sig_func = signal(SIGHUP, SIG_DFL)) != SIG_DFL &&
+ sig_func != SIG_IGN)
+ (*sig_func)(0);
+ else if ( (sig_func = signal(SIGINT, SIG_DFL)) != SIG_DFL &&
+ sig_func != SIG_IGN)
+ (*sig_func)(0);
+ else if ( (sig_func = signal(SIGQUIT, SIG_DFL)) != SIG_DFL &&
+ sig_func != SIG_IGN)
+ (*sig_func)(0);
+ exit(1);
+}
--- /dev/null
+/*
+** halt.h
+**
+*/
+
+void halt();
+
--- /dev/null
+/*
+ * insert.c
+ *
+*/
+
+#include <stdio.h>
+#include <signal.h>
+#include <time.h>
+#include <libpq-fe.h>
+#include "halt.h"
+#include "pginterface.h"
+
+int main(int argc, char **argv)
+{
+ char query[4000];
+ int row =1;
+ int aint;
+ float afloat;
+ double adouble;
+ char achar[11], achar16[17], abpchar[11], avarchar[51], atext[51];
+ time_t aabstime;
+
+ if (argc != 2)
+ halt("Usage: %s database\n",argv[0]);
+
+ connectdb(argv[1],NULL,NULL,NULL,NULL);
+
+ on_error_continue();
+ doquery("DROP TABLE testfetch");
+ on_error_stop();
+
+ doquery("\
+ CREATE TABLE testfetch( \
+ aint int4, \
+ afloat float4, \
+ adouble float8, \
+ achar char, \
+ achar16 char16, \
+ abpchar char(10), \
+ avarchar varchar(50), \
+ atext text, \
+ aabstime abstime) \
+ ");
+
+ while(1)
+ {
+ sprintf(query,"INSERT INTO testfetch VALUES ( \
+ %d, \
+ 2322.12, \
+ '923121.0323'::float8, \
+ 'A', \
+ 'Betty', \
+ 'Charley', \
+ 'Doug', \
+ 'Ernie', \
+ 'now' )", row);
+ doquery(query);
+
+ doquery("BEGIN WORK");
+ doquery("DECLARE c_testfetch BINARY CURSOR FOR \
+ SELECT * FROM testfetch");
+
+ doquery("FETCH ALL IN c_testfetch");
+
+ while (fetch(
+ &aint,
+ &afloat,
+ &adouble,
+ achar,
+ achar16,
+ abpchar,
+ avarchar,
+ atext,
+ &aabstime) != END_OF_TUPLES)
+ printf("int %d\nfloat %f\ndouble %f\nchar %s\nchar16 %s\n\
+bpchar %s\nvarchar %s\ntext %s\nabstime %s",
+ aint,
+ afloat,
+ adouble,
+ achar,
+ achar16,
+ abpchar,
+ avarchar,
+ atext,
+ ctime(&aabstime));
+
+
+ doquery("CLOSE c_testfetch");
+ doquery("COMMIT WORK");
+ printf("--- %-d rows inserted so far\n",row);
+
+ row++;
+ }
+
+ disconnectdb();
+ return 0;
+}
+
--- /dev/null
+/*
+ * pginterface.c
+ *
+*/
+
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <libpq-fe.h>
+#include "halt.h"
+#include "pginterface.h"
+
+static void sig_disconnect();
+static void set_signals();
+
+#define NUL '\0'
+
+/* GLOBAL VARIABLES */
+static PGconn* conn;
+static PGresult* res = NULL;
+
+#define ON_ERROR_STOP 0
+#define ON_ERROR_CONTINUE 1
+
+static int on_error_state = ON_ERROR_STOP;
+
+/* LOCAL VARIABLES */
+static sigset_t block_sigs, unblock_sigs;
+static int tuple;
+
+/*
+**
+** connectdb - returns PGconn structure
+**
+*/
+PGconn *connectdb( char *dbName,
+ char *pghost,
+ char *pgport,
+ char *pgoptions,
+ char *pgtty)
+{
+ /* make a connection to the database */
+ conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
+ if (PQstatus(conn) == CONNECTION_BAD)
+ halt("Connection to database '%s' failed.\n%s\n", dbName,
+ PQerrorMessage(conn));
+ set_signals();
+ return conn;
+}
+
+/*
+**
+** disconnectdb
+**
+*/
+void disconnectdb()
+{
+ PQfinish(conn);
+}
+
+/*
+**
+** doquery - returns PGresult structure
+**
+*/
+PGresult *doquery(char *query)
+{
+ if (res != NULL)
+ PQclear(res);
+
+ sigprocmask(SIG_SETMASK,&block_sigs,NULL);
+ res = PQexec(conn, query);
+ sigprocmask(SIG_SETMASK,&unblock_sigs,NULL);
+
+ if (on_error_state == ON_ERROR_STOP &&
+ (res == NULL ||
+ PQresultStatus(res) == PGRES_BAD_RESPONSE ||
+ PQresultStatus(res) == PGRES_NONFATAL_ERROR ||
+ PQresultStatus(res) == PGRES_FATAL_ERROR))
+ {
+ if (res != NULL)
+ fprintf(stderr,"query error: %s\n",PQcmdStatus(res));
+ else fprintf(stderr,"connection error: %s\n",PQerrorMessage(conn));
+ PQfinish(conn);
+ halt("failed request: %s\n", query);
+ }
+ tuple = 0;
+ return res;
+}
+
+/*
+**
+** fetch - returns tuple number (starts at 0), or the value END_OF_TUPLES
+** NULL pointers are skipped
+**
+*/
+int fetch(void *param, ...)
+{
+ va_list ap;
+ int arg, num_args;
+
+ num_args = PQnfields(res);
+
+ if (tuple >= PQntuples(res))
+ return END_OF_TUPLES;
+
+ va_start(ap, param);
+ for (arg = 0; arg < num_args; arg++)
+ {
+ if (param != NULL)
+ {
+ if (PQfsize(res, arg) == -1)
+ {
+ memcpy(param,PQgetvalue(res,tuple,arg),PQgetlength(res,tuple,arg));
+ ((char *)param)[PQgetlength(res,tuple,arg)] = NUL;
+ }
+ else
+ memcpy(param,PQgetvalue(res,tuple,arg),PQfsize(res,arg));
+ }
+ param = va_arg(ap, char *);
+ }
+ va_end(ap);
+ return tuple++;
+}
+
+/*
+**
+** fetchisnull - returns tuple number (starts at 0), or the value END_OF_TUPLES
+** NULL pointers are skipped
+** Returns true or false into null indicator variables
+*/
+int fetchisnull(void *param, ...)
+{
+ va_list ap;
+ int arg, num_args;
+
+ if (tuple == 0)
+ halt("pginterface:fetchisnull(): You must call fetch() first.\n");
+
+ num_args = PQnfields(res);
+
+ if (tuple-1 >= PQntuples(res))
+ return END_OF_TUPLES;
+ va_start(ap, param);
+ for (arg = 0; arg < num_args; arg++)
+ {
+ if (param != NULL)
+ {
+ if (PQgetisnull(res,tuple-1,arg) != 0)
+ *(int *)param = 1;
+ else
+ *(int *)param = 0;
+ }
+ param = va_arg(ap, char *);
+ }
+ va_end(ap);
+ return tuple-1;
+}
+
+/*
+**
+** on_error_stop
+**
+*/
+void on_error_stop()
+{
+ on_error_state = ON_ERROR_STOP;
+}
+
+/*
+**
+** on_error_continue
+**
+*/
+void on_error_continue()
+{
+ on_error_state = ON_ERROR_CONTINUE;
+}
+
+/*
+**
+** sig_disconnect
+**
+*/
+static void sig_disconnect()
+{
+ fprintf(stderr,"exiting...\n");
+ PQfinish(conn);
+ exit(1);
+}
+
+/*
+**
+** set_signals
+**
+*/
+static void set_signals()
+{
+ sigemptyset(&block_sigs);
+ sigemptyset(&unblock_sigs);
+ sigaddset(&block_sigs,SIGTERM);
+ sigaddset(&block_sigs,SIGHUP);
+ sigaddset(&block_sigs,SIGINT);
+/* sigaddset(&block_sigs,SIGQUIT); no block */
+ sigprocmask(SIG_SETMASK,&unblock_sigs,NULL);
+ signal(SIGTERM,sig_disconnect);
+ signal(SIGHUP,sig_disconnect);
+ signal(SIGINT,sig_disconnect);
+ signal(SIGQUIT,sig_disconnect);
+}
--- /dev/null
+/*
+ * pglib.h
+ *
+*/
+
+PGresult *doquery(char *query);
+PGconn *connectdb();
+void disconnectdb();
+int fetch(void *param, ...);
+int fetchisnull(void *param, ...);
+void on_error_continue();
+void on_error_stop();
+
+#define END_OF_TUPLES (-1)
--- /dev/null
+/*
+ * insert.c
+ *
+*/
+
+/*#define TEST_NON_NULLS*/
+
+#include <stdio.h>
+#include <signal.h>
+#include <time.h>
+#include <libpq-fe.h>
+#include "halt.h"
+#include "pginterface.h"
+
+int main(int argc, char **argv)
+{
+ char query[4000];
+ int row =1;
+ int aint;
+ float afloat;
+ double adouble;
+ char achar[11], achar16[17], abpchar[11], avarchar[51], atext[51];
+ time_t aabstime;
+ int aint_null,
+ afloat_null,
+ adouble_null,
+ achar_null,
+ achar16_null,
+ abpchar_null,
+ avarchar_null,
+ atext_null,
+ aabstime_null;
+
+ if (argc != 2)
+ halt("Usage: %s database\n",argv[0]);
+
+ connectdb(argv[1],NULL,NULL,NULL,NULL);
+
+ on_error_continue();
+ doquery("DROP TABLE testfetch");
+ on_error_stop();
+
+ doquery("\
+ CREATE TABLE testfetch( \
+ aint int4, \
+ afloat float4, \
+ adouble float8, \
+ achar char, \
+ achar16 char16, \
+ abpchar char(10), \
+ avarchar varchar(50), \
+ atext text, \
+ aabstime abstime) \
+ ");
+
+#ifdef TEST_NON_NULLS
+ sprintf(query,"INSERT INTO testfetch VALUES ( \
+ 0, \
+ 0, \
+ 0, \
+ '', \
+ '', \
+ '', \
+ '', \
+ '', \
+ '');");
+#else
+ sprintf(query,"INSERT INTO testfetch VALUES ( \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL);");
+#endif
+ doquery(query);
+
+ doquery("BEGIN WORK");
+ doquery("DECLARE c_testfetch BINARY CURSOR FOR \
+ SELECT * FROM testfetch");
+
+ doquery("FETCH ALL IN c_testfetch");
+
+ if (fetch(
+ &aint,
+ &afloat,
+ &adouble,
+ achar,
+ achar16,
+ abpchar,
+ avarchar,
+ atext,
+ &aabstime) != END_OF_TUPLES)
+ printf("int %d\nfloat %f\ndouble %f\nchar %s\nchar16 %s\n\
+bpchar %s\nvarchar %s\ntext %s\nabstime %s\n",
+ aint,
+ afloat,
+ adouble,
+ achar,
+ achar16,
+ abpchar,
+ avarchar,
+ atext,
+ ctime(&aabstime));
+ if (fetchisnull(
+ &aint_null,
+ &afloat_null,
+ &adouble_null,
+ &achar_null,
+ &achar16_null,
+ &abpchar_null,
+ &avarchar_null,
+ &atext_null,
+ &aabstime_null) != END_OF_TUPLES)
+ printf("NULL:\nint %d\nfloat %d\ndouble %d\nchar %d\nchar16 %d\n\
+bpchar %d\nvarchar %d\ntext %d\nabstime %d\n",
+ aint_null,
+ afloat_null,
+ adouble_null,
+ achar_null,
+ achar16_null,
+ abpchar_null,
+ avarchar_null,
+ atext_null,
+ aabstime_null);
+
+
+ doquery("CLOSE c_testfetch");
+ doquery("COMMIT WORK");
+ printf("--- 1 row inserted\n");
+
+ row++;
+
+ disconnectdb();
+ return 0;
+}
+
--- /dev/null
+/*
+ * wordcount.c
+ *
+*/
+
+#include <stdio.h>
+#include <signal.h>
+#include <time.h>
+#include <libpq-fe.h>
+#include "halt.h"
+#include "pginterface.h"
+
+int main(int argc, char **argv)
+{
+ char query[4000];
+ int row = 0;
+ int count;
+ char line[4000];
+
+ if (argc != 2)
+ halt("Usage: %s database\n",argv[0]);
+
+ connectdb(argv[1],NULL,NULL,NULL,NULL);
+ on_error_continue();
+ doquery("DROP TABLE words");
+ on_error_stop();
+
+ doquery("\
+ CREATE TABLE words( \
+ matches int4, \
+ word text ) \
+ ");
+ doquery("\
+ CREATE INDEX i_words_1 ON words USING btree ( \
+ word text_ops )\
+ ");
+
+ while(1)
+ {
+ if (scanf("%s",line) != 1)
+ break;
+ doquery("BEGIN WORK");
+ sprintf(query,"\
+ DECLARE c_words BINARY CURSOR FOR \
+ SELECT count(*) \
+ FROM words \
+ WHERE word = '%s'", line);
+ doquery(query);
+ doquery("FETCH ALL IN c_words");
+
+ while (fetch(&count) == END_OF_TUPLES)
+ count = 0;
+ doquery("CLOSE c_words");
+ doquery("COMMIT WORK");
+
+ if (count == 0)
+ sprintf(query,"\
+ INSERT INTO words \
+ VALUES (1, '%s')", line);
+ else
+ sprintf(query,"\
+ UPDATE words \
+ SET matches = matches + 1
+ WHERE word = '%s'", line);
+ doquery(query);
+ row++;
+ }
+
+ disconnectdb();
+ return 0;
+}
+