aboutsummaryrefslogtreecommitdiff
path: root/src/interfaces
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces')
-rw-r--r--src/interfaces/libpgtcl/Makefile38
-rw-r--r--src/interfaces/libpgtcl/README7
-rw-r--r--src/interfaces/libpgtcl/libpgtcl.h21
-rw-r--r--src/interfaces/libpgtcl/pgtcl.c105
-rw-r--r--src/interfaces/libpgtcl/pgtclCmds.c812
-rw-r--r--src/interfaces/libpgtcl/pgtclCmds.h52
-rw-r--r--src/interfaces/libpgtcl/pgtclId.c51
-rw-r--r--src/interfaces/libpgtcl/pgtclId.h18
-rw-r--r--src/interfaces/libpq++/Makefile54
-rw-r--r--src/interfaces/libpq++/README22
-rw-r--r--src/interfaces/libpq++/examples/Makefile70
-rw-r--r--src/interfaces/libpq++/examples/testlibpq0.cc49
-rw-r--r--src/interfaces/libpq++/examples/testlibpq1.cc84
-rw-r--r--src/interfaces/libpq++/examples/testlibpq2.cc71
-rw-r--r--src/interfaces/libpq++/examples/testlibpq2.sql5
-rw-r--r--src/interfaces/libpq++/examples/testlibpq3.cc131
-rw-r--r--src/interfaces/libpq++/examples/testlibpq3.sql6
-rw-r--r--src/interfaces/libpq++/examples/testlibpq4.cc69
-rw-r--r--src/interfaces/libpq++/examples/testlo.cc63
-rw-r--r--src/interfaces/libpq++/libpq++.H173
-rw-r--r--src/interfaces/libpq++/man/libpq++.3434
-rw-r--r--src/interfaces/libpq++/pgconnection.cc94
-rw-r--r--src/interfaces/libpq++/pgenv.cc109
-rw-r--r--src/interfaces/libpq++/pglobject.cc152
-rw-r--r--src/interfaces/libpq/Makefile98
-rw-r--r--src/interfaces/libpq/README1
-rw-r--r--src/interfaces/libpq/fe-auth.c544
-rw-r--r--src/interfaces/libpq/fe-auth.h38
-rw-r--r--src/interfaces/libpq/fe-connect.c460
-rw-r--r--src/interfaces/libpq/fe-exec.c1061
-rw-r--r--src/interfaces/libpq/fe-lobj.c381
-rw-r--r--src/interfaces/libpq/fe-misc.c193
-rw-r--r--src/interfaces/libpq/libpq-fe.h251
-rw-r--r--src/interfaces/libpq/pg_hba13
-rw-r--r--src/interfaces/libpq/pqsignal.c40
-rw-r--r--src/interfaces/libpq/pqsignal.h32
36 files changed, 5802 insertions, 0 deletions
diff --git a/src/interfaces/libpgtcl/Makefile b/src/interfaces/libpgtcl/Makefile
new file mode 100644
index 00000000000..73e218b3842
--- /dev/null
+++ b/src/interfaces/libpgtcl/Makefile
@@ -0,0 +1,38 @@
+#-------------------------------------------------------------------------
+#
+# Makefile
+# Makefile for libpgtcl library
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+# $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $
+#
+#-------------------------------------------------------------------------
+
+LIB= pgtcl
+
+MKDIR= ../mk
+include $(MKDIR)/postgres.mk
+
+CFLAGS+= -I$(HEADERDIR) \
+ -I$(srcdir)/backend/include \
+ -I$(srcdir)/backend \
+ -I$(CURDIR) \
+ -I$(TCL_INCDIR)
+
+ifdef KRBVERS
+CFLAGS+= $(KRBFLAGS)
+endif
+
+LIBSRCS= pgtcl.c pgtclCmds.c pgtclId.c
+
+install-headers:
+ $(INSTALL) $(INSTLOPTS) libpgtcl.h $(HEADERDIR)/libpgtcl.h
+
+
+install:: install-headers
+
+include $(MKDIR)/postgres.lib.mk
+
diff --git a/src/interfaces/libpgtcl/README b/src/interfaces/libpgtcl/README
new file mode 100644
index 00000000000..d2e2d59c798
--- /dev/null
+++ b/src/interfaces/libpgtcl/README
@@ -0,0 +1,7 @@
+libpgtcl is a library that implements Tcl commands for front-end
+clients to interact with the Postgres95 backend. See libpgtcl.doc for
+details.
+
+For an example of how to build a new tclsh to use libpgtcl, see the
+directory ../bin/pgtclsh
+
diff --git a/src/interfaces/libpgtcl/libpgtcl.h b/src/interfaces/libpgtcl/libpgtcl.h
new file mode 100644
index 00000000000..923bf594d70
--- /dev/null
+++ b/src/interfaces/libpgtcl/libpgtcl.h
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpgtcl.h--
+ * libpgtcl is a tcl package for front-ends to interface with pglite
+ * It's the tcl equivalent of the old libpq C interface.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: libpgtcl.h,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef LIBPGTCL_H
+#define LIBPGTCL_H
+
+#include "tcl.h"
+
+extern int Pg_Init (Tcl_Interp *interp);
+
+#endif /* LIBPGTCL_H */
diff --git a/src/interfaces/libpgtcl/pgtcl.c b/src/interfaces/libpgtcl/pgtcl.c
new file mode 100644
index 00000000000..449107339fe
--- /dev/null
+++ b/src/interfaces/libpgtcl/pgtcl.c
@@ -0,0 +1,105 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgtcl.c--
+ *
+ * libpgtcl is a tcl package for front-ends to interface with pglite
+ * It's the tcl equivalent of the old libpq C interface.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtcl.c,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "tcl.h"
+#include "libpgtcl.h"
+#include "pgtclCmds.h"
+
+/*
+ * PG_Init
+ * initialization package for the PGLITE Tcl package
+ *
+ */
+
+int
+Pg_Init (Tcl_Interp *interp)
+{
+ /* register all pgtcl commands */
+
+ Tcl_CreateCommand(interp,
+ "pg_connect",
+ Pg_connect,
+ (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+ Tcl_CreateCommand(interp,
+ "pg_disconnect",
+ Pg_disconnect,
+ (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+ Tcl_CreateCommand(interp,
+ "pg_exec",
+ Pg_exec,
+ (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+ Tcl_CreateCommand(interp,
+ "pg_result",
+ Pg_result,
+ (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+ Tcl_CreateCommand(interp,
+ "pg_lo_open",
+ Pg_lo_open,
+ (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+ Tcl_CreateCommand(interp,
+ "pg_lo_close",
+ Pg_lo_close,
+ (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+ Tcl_CreateCommand(interp,
+ "pg_lo_read",
+ Pg_lo_read,
+ (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+ Tcl_CreateCommand(interp,
+ "pg_lo_write",
+ Pg_lo_write,
+ (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+ Tcl_CreateCommand(interp,
+ "pg_lo_lseek",
+ Pg_lo_lseek,
+ (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+ Tcl_CreateCommand(interp,
+ "pg_lo_creat",
+ Pg_lo_creat,
+ (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+ Tcl_CreateCommand(interp,
+ "pg_lo_tell",
+ Pg_lo_tell,
+ (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+ Tcl_CreateCommand(interp,
+ "pg_lo_unlink",
+ Pg_lo_unlink,
+ (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+ Tcl_CreateCommand(interp,
+ "pg_lo_import",
+ Pg_lo_import,
+ (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+ Tcl_CreateCommand(interp,
+ "pg_lo_export",
+ Pg_lo_export,
+ (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+ return TCL_OK;
+}
+
+
diff --git a/src/interfaces/libpgtcl/pgtclCmds.c b/src/interfaces/libpgtcl/pgtclCmds.c
new file mode 100644
index 00000000000..e6c2c539118
--- /dev/null
+++ b/src/interfaces/libpgtcl/pgtclCmds.c
@@ -0,0 +1,812 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgtclCmds.c--
+ * C functions which implement pg_* tcl commands
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclCmds.c,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <tcl.h>
+#include <string.h>
+#include "libpq/pqcomm.h"
+#include "libpq-fe.h"
+#include "libpq/libpq-fs.h"
+#include "pgtclCmds.h"
+#include "pgtclId.h"
+
+/**********************************
+ * pg_connect
+ make a connection to a backend.
+
+ syntax:
+ pg_connect dbName [-host hostName] [-port portNumber] [-tty pqtty]]
+
+ the return result is either an error message or a handle for a database
+ connection. Handles start with the prefix "pgp"
+
+ **********************************/
+
+int
+Pg_connect(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+ char *pghost = NULL;
+ char *pgtty = NULL;
+ char *pgport = NULL;
+ char *pgoptions = NULL;
+ char *dbName;
+ int i;
+ PGconn *conn;
+
+ if (argc == 1) {
+ Tcl_AppendResult(interp, "pg_connect: database name missing\n", 0);
+ Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]", 0);
+ return TCL_ERROR;
+
+ }
+ if (argc > 2) {
+ /* parse for pg environment settings */
+ i = 2;
+ while (i+1 < argc) {
+ if (strcmp(argv[i], "-host") == 0) {
+ pghost = argv[i+1];
+ i += 2;
+ }
+ else
+ if (strcmp(argv[i], "-port") == 0) {
+ pgport = argv[i+1];
+ i += 2;
+ }
+ else
+ if (strcmp(argv[i], "-tty") == 0) {
+ pgtty = argv[i+1];
+ i += 2;
+ }
+ else if (strcmp(argv[i], "-options") == 0) {
+ pgoptions = argv[i+1];
+ i += 2;
+ }
+ else {
+ Tcl_AppendResult(interp, "Bad option to pg_connect : \n",
+ argv[i], 0);
+ Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]",0);
+ return TCL_ERROR;
+ }
+ } /* while */
+ if ((i % 2 != 0) || i != argc) {
+ Tcl_AppendResult(interp, "wrong # of arguments to pg_connect\n", argv[i],0);
+ Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]",0);
+ return TCL_ERROR;
+ }
+ }
+ dbName = argv[1];
+
+ conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
+ if (conn->status == CONNECTION_OK) {
+ PgSetId(interp->result, (void*)conn);
+ return TCL_OK;
+ }
+ else {
+ Tcl_AppendResult(interp, "Connection to ", dbName, " failed\n", 0);
+ Tcl_AppendResult(interp, conn->errorMessage, 0);
+ return TCL_ERROR;
+ }
+}
+
+
+/**********************************
+ * pg_disconnect
+ close a backend connection
+
+ syntax:
+ pg_disconnect connection
+
+ The argument passed in must be a connection pointer.
+
+ **********************************/
+
+int
+Pg_disconnect(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+ PGconn *conn;
+ char* connPtrName;
+
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "Wrong # of arguments\n", "pg_disconnect connection", 0);
+ return TCL_ERROR;
+ }
+
+ connPtrName = argv[1];
+ if (! PgValidId(connPtrName)) {
+ Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
+ return TCL_ERROR;
+ }
+
+ conn = (PGconn*)PgGetId(connPtrName);
+ PQfinish(conn);
+ return TCL_OK;
+}
+
+/**********************************
+ * pg_exec
+ send a query string to the backend connection
+
+ syntax:
+ pg_exec connection query
+
+ the return result is either an error message or a handle for a query
+ result. Handles start with the prefix "pgp"
+ **********************************/
+
+int
+Pg_exec(AlientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+ PGconn *conn;
+ PGresult *result;
+ char* connPtrName;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "Wrong # of arguments\n",
+ "pg_exec connection queryString", 0);
+ return TCL_ERROR;
+ }
+ connPtrName = argv[1];
+
+ if (! PgValidId(connPtrName)) {
+ Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+ return TCL_ERROR;
+ }
+
+ conn = (PGconn*)PgGetId(connPtrName);
+ result = PQexec(conn, argv[2]);
+ if (result) {
+ PgSetId(interp->result, (void*)result);
+ return TCL_OK;
+ }
+ else {
+ /* error occurred during the query */
+ Tcl_SetResult(interp, conn->errorMessage, TCL_STATIC);
+ return TCL_ERROR;
+ }
+ /* check return status of result */
+ return TCL_OK;
+}
+
+/**********************************
+ * pg_result
+ get information about the results of a query
+
+ syntax:
+ pg_result result ?option?
+
+ the options are:
+ -status
+ the status of the result
+ -conn
+ the connection that produced the result
+ -assign arrayName
+ assign the results to an array
+ -numTuples
+ the number of tuples in the query
+ -attributes
+ returns a list of the name/type pairs of the tuple attributes
+ -getTuple tupleNumber
+ returns the values of the tuple in a list
+ -clear
+ clear the result buffer. Do not reuse after this
+ **********************************/
+int
+Pg_result(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+ char* resultPtrName;
+ PGresult *result;
+ char *opt;
+ int i;
+ int tupno;
+ char arrayInd[MAX_MESSAGE_LEN];
+ char *arrVar;
+
+ if (argc != 3 && argc != 4) {
+ Tcl_AppendResult(interp, "Wrong # of arguments\n",0);
+ goto Pg_result_errReturn;
+ }
+
+ resultPtrName = argv[1];
+ if (! PgValidId(resultPtrName)) {
+ Tcl_AppendResult(interp, "First argument is not a valid query result\n", 0);
+ return TCL_ERROR;
+ }
+
+ result = (PGresult*)PgGetId(resultPtrName);
+ opt = argv[2];
+
+ if (strcmp(opt, "-status") == 0) {
+ Tcl_AppendResult(interp, pgresStatus[PQresultStatus(result)], 0);
+ return TCL_OK;
+ }
+ else if (strcmp(opt, "-oid") == 0) {
+ Tcl_AppendResult(interp, PQoidStatus(result), 0);
+ return TCL_OK;
+ }
+ else if (strcmp(opt, "-conn") == 0) {
+ PgSetId(interp->result, (void*)result->conn);
+ return TCL_OK;
+ }
+ else if (strcmp(opt, "-clear") == 0) {
+ PQclear(result);
+ return TCL_OK;
+ }
+ else if (strcmp(opt, "-numTuples") == 0) {
+ sprintf(interp->result, "%d", PQntuples(result));
+ return TCL_OK;
+ }
+ else if (strcmp(opt, "-assign") == 0) {
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "-assign option must be followed by a variable name",0);
+ return TCL_ERROR;
+ }
+ arrVar = argv[3];
+ /* this assignment assigns the table of result tuples into a giant
+ array with the name given in the argument,
+ the indices of the array or (tupno,attrName)*/
+ for (tupno = 0; tupno<PQntuples(result); tupno++) {
+ for (i=0;i<PQnfields(result);i++) {
+ sprintf(arrayInd, "%d,%s", tupno, PQfname(result,i));
+ Tcl_SetVar2(interp, arrVar, arrayInd,
+ PQgetvalue(result,tupno,i),
+ TCL_LEAVE_ERR_MSG);
+ }
+ }
+ Tcl_AppendResult(interp, arrVar, 0);
+ return TCL_OK;
+ }
+ else if (strcmp(opt, "-getTuple") == 0) {
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "-getTuple option must be followed by a tuple number",0);
+ return TCL_ERROR;
+ }
+ tupno = atoi(argv[3]);
+
+ if (tupno >= PQntuples(result)) {
+ Tcl_AppendResult(interp, "argument to getTuple cannot exceed number of tuples - 1",0);
+ return TCL_ERROR;
+ }
+
+/* Tcl_AppendResult(interp, PQgetvalue(result,tupno,0),NULL); */
+ Tcl_AppendElement(interp, PQgetvalue(result,tupno,0));
+ for (i=1;i<PQnfields(result);i++) {
+/* Tcl_AppendResult(interp, " ", PQgetvalue(result,tupno,i),NULL);*/
+ Tcl_AppendElement(interp, PQgetvalue(result,tupno,i));
+ }
+ return TCL_OK;
+ }
+ else if (strcmp(opt, "-attributes") == 0) {
+ Tcl_AppendResult(interp, PQfname(result,0),NULL);
+ for (i=1;i<PQnfields(result);i++) {
+ Tcl_AppendResult(interp, " ", PQfname(result,i), NULL);
+ }
+ return TCL_OK;
+ }
+ else {
+ Tcl_AppendResult(interp, "Invalid option",0);
+ goto Pg_result_errReturn;
+ }
+
+
+ Pg_result_errReturn:
+ Tcl_AppendResult(interp,
+ "pg_result result ?option? where ?option is\n",
+ "\t-status\n",
+ "\t-conn\n",
+ "\t-assign arrayVarName\n",
+ "\t-numTuples\n",
+ "\t-attributes\n"
+ "\t-getTuple tupleNumber\n",
+ "\t-clear\n",
+ "\t-oid\n",
+ 0);
+ return TCL_ERROR;
+
+
+}
+
+/**********************************
+ * pg_lo_open
+ open a large object
+
+ syntax:
+ pg_lo_open conn objOid mode
+
+ where mode can be either 'r', 'w', or 'rw'
+**********************/
+
+int
+Pg_lo_open(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+ PGconn *conn;
+ char* connPtrName;
+ int lobjId;
+ int mode;
+ int fd;
+
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "Wrong # of arguments\n",
+ "pg_lo_open connection lobjOid mode", 0);
+ return TCL_ERROR;
+ }
+ connPtrName = argv[1];
+ if (! PgValidId(connPtrName)) {
+ Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+ return TCL_ERROR;
+ }
+
+ conn = (PGconn*)PgGetId(connPtrName);
+ lobjId = atoi(argv[2]);
+ if (strlen(argv[3]) < 1 ||
+ strlen(argv[3]) > 2)
+ {
+ Tcl_AppendResult(interp,"mode argument must be 'r', 'w', or 'rw'",0);
+ return TCL_ERROR;
+ }
+ switch (argv[3][0]) {
+ case 'r':
+ case 'R':
+ mode = INV_READ;
+ break;
+ case 'w':
+ case 'W':
+ mode = INV_WRITE;
+ break;
+ default:
+ Tcl_AppendResult(interp,"mode argument must be 'r', 'w', or 'rw'",0);
+ return TCL_ERROR;
+ }
+ switch (argv[3][1]) {
+ case '\0':
+ break;
+ case 'r':
+ case 'R':
+ mode = mode & INV_READ;
+ break;
+ case 'w':
+ case 'W':
+ mode = mode & INV_WRITE;
+ break;
+ default:
+ Tcl_AppendResult(interp,"mode argument must be 'r', 'w', or 'rw'",0);
+ return TCL_ERROR;
+ }
+
+ fd = lo_open(conn,lobjId,mode);
+ sprintf(interp->result,"%d",fd);
+ return TCL_OK;
+}
+
+/**********************************
+ * pg_lo_close
+ close a large object
+
+ syntax:
+ pg_lo_close conn fd
+
+**********************/
+int
+Pg_lo_close(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+ PGconn *conn;
+ char* connPtrName;
+ int fd;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "Wrong # of arguments\n",
+ "pg_lo_close connection fd", 0);
+ return TCL_ERROR;
+ }
+
+ connPtrName = argv[1];
+ if (! PgValidId(connPtrName)) {
+ Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+ return TCL_ERROR;
+ }
+
+ conn = (PGconn*)PgGetId(connPtrName);
+ fd = atoi(argv[2]);
+ sprintf(interp->result,"%d",lo_close(conn,fd));
+ return TCL_OK;
+}
+
+/**********************************
+ * pg_lo_read
+ reads at most len bytes from a large object into a variable named
+ bufVar
+
+ syntax:
+ pg_lo_read conn fd bufVar len
+
+ bufVar is the name of a variable in which to store the contents of the read
+
+**********************/
+int
+Pg_lo_read(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+ PGconn *conn;
+ char* connPtrName;
+ int fd;
+ int nbytes = 0;
+ char *buf;
+ char *bufVar;
+ int len;
+
+ if (argc != 5) {
+ Tcl_AppendResult(interp, "Wrong # of arguments\n",
+ " pg_lo_read conn fd bufVar len", 0);
+ return TCL_ERROR;
+ }
+
+ connPtrName = argv[1];
+ if (! PgValidId(connPtrName)) {
+ Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+ return TCL_ERROR;
+ }
+
+ conn = (PGconn*)PgGetId(connPtrName);
+ fd = atoi(argv[2]);
+
+ bufVar = argv[3];
+
+ len = atoi(argv[4]);
+
+ if (len <= 0) {
+ sprintf(interp->result,"%d",nbytes);
+ return TCL_OK;
+ }
+ buf = malloc(sizeof(len+1));
+
+ nbytes = lo_read(conn,fd,buf,len);
+
+ Tcl_SetVar(interp,bufVar,buf,TCL_LEAVE_ERR_MSG);
+ sprintf(interp->result,"%d",nbytes);
+ free(buf);
+ return TCL_OK;
+
+}
+
+/***********************************
+Pg_lo_write
+ write at most len bytes to a large object
+
+ syntax:
+ pg_lo_write conn fd buf len
+
+***********************************/
+int
+Pg_lo_write(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+ PGconn *conn;
+ char *connPtrName;
+ char *buf;
+ int fd;
+ int nbytes = 0;
+ int len;
+
+ if (argc != 5) {
+ Tcl_AppendResult(interp, "Wrong # of arguments\n",
+ "pg_lo_write conn fd buf len", 0);
+ return TCL_ERROR;
+ }
+
+ connPtrName = argv[1];
+ if (! PgValidId(connPtrName)) {
+ Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+ return TCL_ERROR;
+ }
+
+ conn = (PGconn*)PgGetId(connPtrName);
+ fd = atoi(argv[2]);
+
+ buf = argv[3];
+
+ len = atoi(argv[4]);
+
+ if (len <= 0) {
+ sprintf(interp->result,"%d",nbytes);
+ return TCL_OK;
+ }
+
+ nbytes = lo_write(conn,fd,buf,len);
+ sprintf(interp->result,"%d",nbytes);
+ return TCL_OK;
+}
+
+/***********************************
+Pg_lo_lseek
+ seek to a certain position in a large object
+
+syntax
+ pg_lo_lseek conn fd offset whence
+
+whence can be either
+"SEEK_CUR", "SEEK_END", or "SEEK_SET"
+***********************************/
+int
+Pg_lo_lseek(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+ PGconn *conn;
+ char* connPtrName;
+ int fd;
+ char *whenceStr;
+ int offset, whence;
+
+ if (argc != 5) {
+ Tcl_AppendResult(interp, "Wrong # of arguments\n",
+ "pg_lo_lseek conn fd offset whence", 0);
+ return TCL_ERROR;
+ }
+
+ connPtrName = argv[1];
+ if (! PgValidId(connPtrName)) {
+ Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+ return TCL_ERROR;
+ }
+
+ conn = (PGconn*)PgGetId(connPtrName);
+ fd = atoi(argv[2]);
+
+ offset = atoi(argv[3]);
+
+ whenceStr = argv[4];
+ if (strcmp(whenceStr,"SEEK_SET") == 0) {
+ whence = SEEK_SET;
+ } else if (strcmp(whenceStr,"SEEK_CUR") == 0) {
+ whence = SEEK_CUR;
+ } else if (strcmp(whenceStr,"SEEK_END") == 0) {
+ whence = SEEK_END;
+ } else {
+ Tcl_AppendResult(interp, "the whence argument to Pg_lo_lseek must be SEEK_SET, SEEK_CUR or SEEK_END",0);
+ return TCL_ERROR;
+ }
+
+ sprintf(interp->result,"%d",lo_lseek(conn,fd,offset,whence));
+ return TCL_OK;
+}
+
+
+/***********************************
+Pg_lo_creat
+ create a new large object with mode
+
+ syntax:
+ pg_lo_creat conn mode
+
+mode can be any OR'ing together of INV_READ, INV_WRITE, and INV_ARCHIVE,
+for now, we don't support any additional storage managers.
+
+***********************************/
+int
+Pg_lo_creat(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+ PGconn *conn;
+ char* connPtrName;
+ char *modeStr;
+ char *modeWord;
+ int mode;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "Wrong # of arguments\n",
+ "pg_lo_creat conn mode", 0);
+ return TCL_ERROR;
+ }
+
+ connPtrName = argv[1];
+ if (! PgValidId(connPtrName)) {
+ Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+ return TCL_ERROR;
+ }
+
+ conn = (PGconn*)PgGetId(connPtrName);
+
+ modeStr = argv[2];
+
+ modeWord = strtok(modeStr,"|");
+ if (strcmp(modeWord,"INV_READ") == 0) {
+ mode = INV_READ;
+ } else if (strcmp(modeWord,"INV_WRITE") == 0) {
+ mode = INV_WRITE;
+ } else if (strcmp(modeWord,"INV_ARCHIVE") == 0) {
+ mode = INV_ARCHIVE;
+ } else {
+ Tcl_AppendResult(interp,
+ "invalid mode argument to Pg_lo_creat\nmode argument must be some OR'd combination of INV_READ, INV_WRITE, and INV_ARCHIVE",
+ 0);
+ return TCL_ERROR;
+ }
+
+ while ( (modeWord = strtok((char*)NULL, "|")) != NULL) {
+ if (strcmp(modeWord,"INV_READ") == 0) {
+ mode |= INV_READ;
+ } else if (strcmp(modeWord,"INV_WRITE") == 0) {
+ mode |= INV_WRITE;
+ } else if (strcmp(modeWord,"INV_ARCHIVE") == 0) {
+ mode |= INV_ARCHIVE;
+ } else {
+ Tcl_AppendResult(interp,
+ "invalid mode argument to Pg_lo_creat\nmode argument must be some OR'd combination of INV_READ, INV_WRITE, and INV_ARCHIVE",
+ 0);
+ return TCL_ERROR;
+ }
+ }
+ sprintf(interp->result,"%d",lo_creat(conn,mode));
+ return TCL_OK;
+}
+
+/***********************************
+Pg_lo_tell
+ returns the current seek location of the large object
+
+ syntax:
+ pg_lo_tell conn fd
+
+***********************************/
+int
+Pg_lo_tell(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+ PGconn *conn;
+ char* connPtrName;
+ int fd;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "Wrong # of arguments\n",
+ "pg_lo_tell conn fd", 0);
+ return TCL_ERROR;
+ }
+
+ connPtrName = argv[1];
+ if (! PgValidId(connPtrName)) {
+ Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+ return TCL_ERROR;
+ }
+
+ conn = (PGconn*)PgGetId(connPtrName);
+ fd = atoi(argv[2]);
+
+ sprintf(interp->result,"%d",lo_tell(conn,fd));
+ return TCL_OK;
+
+}
+
+/***********************************
+Pg_lo_unlink
+ unlink a file based on lobject id
+
+ syntax:
+ pg_lo_unlink conn lobjId
+
+
+***********************************/
+int
+Pg_lo_unlink(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+ PGconn *conn;
+ char* connPtrName;
+ int lobjId;
+ int retval;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "Wrong # of arguments\n",
+ "pg_lo_tell conn fd", 0);
+ return TCL_ERROR;
+ }
+
+ connPtrName = argv[1];
+ if (! PgValidId(connPtrName)) {
+ Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+ return TCL_ERROR;
+ }
+
+ conn = (PGconn*)PgGetId(connPtrName);
+ lobjId = atoi(argv[2]);
+
+ retval = lo_unlink(conn,lobjId);
+ if (retval == -1) {
+ sprintf(interp->result,"Pg_lo_unlink of '%d' failed",lobjId);
+ return TCL_ERROR;
+ }
+
+ sprintf(interp->result,"%d",retval);
+ return TCL_OK;
+}
+
+/***********************************
+Pg_lo_import
+ import a Unix file into an (inversion) large objct
+ returns the oid of that object upon success
+ returns InvalidOid upon failure
+
+ syntax:
+ pg_lo_import conn filename
+
+***********************************/
+
+int
+Pg_lo_import(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+ PGconn *conn;
+ char* connPtrName;
+ char* filename;
+ Oid lobjId;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "Wrong # of arguments\n",
+ "pg_lo_import conn filename", 0);
+ return TCL_ERROR;
+ }
+
+ connPtrName = argv[1];
+ if (! PgValidId(connPtrName)) {
+ Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+ return TCL_ERROR;
+ }
+
+ conn = (PGconn*)PgGetId(connPtrName);
+ filename = argv[2];
+
+ lobjId = lo_import(conn,filename);
+ if (lobjId == InvalidOid) {
+ sprintf(interp->result, "Pg_lo_import of '%s' failed",filename);
+ return TCL_ERROR;
+ }
+ sprintf(interp->result,"%d",lobjId);
+ return TCL_OK;
+}
+
+/***********************************
+Pg_lo_export
+ export an Inversion large object to a Unix file
+
+ syntax:
+ pg_lo_export conn lobjId filename
+
+***********************************/
+
+int
+Pg_lo_export(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+ PGconn *conn;
+ char* connPtrName;
+ char* filename;
+ Oid lobjId;
+ int retval;
+
+ if (argc != 4) {
+ Tcl_AppendResult(interp, "Wrong # of arguments\n",
+ "pg_lo_export conn lobjId filename", 0);
+ return TCL_ERROR;
+ }
+
+ connPtrName = argv[1];
+ if (! PgValidId(connPtrName)) {
+ Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+ return TCL_ERROR;
+ }
+
+ conn = (PGconn*)PgGetId(connPtrName);
+ lobjId = atoi(argv[2]);
+ filename = argv[3];
+
+ retval = lo_export(conn,lobjId,filename);
+ if (retval == -1) {
+ sprintf(interp->result, "Pg_lo_export %d %s failed",lobjId, filename);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
diff --git a/src/interfaces/libpgtcl/pgtclCmds.h b/src/interfaces/libpgtcl/pgtclCmds.h
new file mode 100644
index 00000000000..244471ebe1b
--- /dev/null
+++ b/src/interfaces/libpgtcl/pgtclCmds.h
@@ -0,0 +1,52 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgtclCmds.h--
+ * declarations for the C functions which implement pg_* tcl commands
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: pgtclCmds.h,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef PGTCLCMDS_H
+#define PGTCLCMDS_H
+
+#include "tcl.h"
+
+/* **************************/
+/* registered Tcl functions */
+/* **************************/
+extern int Pg_connect(
+ ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_disconnect(
+ ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_exec(
+ ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_result(
+ ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_open(
+ ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_close(
+ ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_read(
+ ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_write(
+ ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_lseek(
+ ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_creat(
+ ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_tell(
+ ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_unlink(
+ ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_import(
+ ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_export(
+ ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+
+
+#endif /*PGTCLCMDS_H*/
+
diff --git a/src/interfaces/libpgtcl/pgtclId.c b/src/interfaces/libpgtcl/pgtclId.c
new file mode 100644
index 00000000000..00dffe7a883
--- /dev/null
+++ b/src/interfaces/libpgtcl/pgtclId.c
@@ -0,0 +1,51 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgtclId.c--
+ * useful routines to convert between strings and pointers
+ * Needed because everything in tcl is a string, but we want pointers
+ * to data structures
+ *
+ * ASSUMPTION: sizeof(long) >= sizeof(void*)
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclId.c,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "tcl.h"
+
+#include "pgtclId.h"
+
+/* convert a pointer into a string */
+void
+PgSetId(char *id, void *ptr)
+{
+ (void) sprintf(id, "pgp%lx", (long) ptr);
+}
+
+
+/* get back a pointer from a string */
+void *
+PgGetId(char *id)
+{
+ long ptr;
+ ptr = strtol(id+3, NULL, 16);
+ return (void *) ptr;
+}
+
+/* check to see if the string is a valid pgtcl pointer */
+int
+PgValidId(char* id)
+{
+ if ( (strlen(id) > 3) && id[0]=='p' && id[1] == 'g' && id[2] == 'p')
+ return 1;
+ else
+ return 0;
+}
diff --git a/src/interfaces/libpgtcl/pgtclId.h b/src/interfaces/libpgtcl/pgtclId.h
new file mode 100644
index 00000000000..af9839ceb1e
--- /dev/null
+++ b/src/interfaces/libpgtcl/pgtclId.h
@@ -0,0 +1,18 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgtclId.h--
+ * useful routines to convert between strings and pointers
+ * Needed because everything in tcl is a string, but often, pointers
+ * to data structures are needed.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: pgtclId.h,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+extern void PgSetId(char *id, void *ptr);
+extern void* PgGetId(char *id);
+extern int PgValidId(char* id);
diff --git a/src/interfaces/libpq++/Makefile b/src/interfaces/libpq++/Makefile
new file mode 100644
index 00000000000..e1d58847ee5
--- /dev/null
+++ b/src/interfaces/libpq++/Makefile
@@ -0,0 +1,54 @@
+#-------------------------------------------------------------------------
+#
+# Makefile
+# Makefile for libpq++ library
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+# $Header: /cvsroot/pgsql/src/interfaces/libpq++/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:18 scrappy Exp $
+#
+#-------------------------------------------------------------------------
+
+CPP_LIB= true
+
+LIB= pq++
+
+MKDIR= ../mk
+include $(MKDIR)/postgres.mk
+
+CXXFLAGS = $(CFLAGS)
+
+CXXFLAGS+= -I$(srcdir)/backend/include \
+ -I$(srcdir)/backend \
+ -I$(srcdir)/libpq \
+ -I$(CURDIR) \
+
+ifdef KRBVERS
+CXXFLAGS+= $(KRBFLAGS)
+endif
+
+
+LIBSRCS = pgenv.cc pgconnection.cc pglobject.cc
+
+.PHONY: beforeinstall-headers install-headers
+
+ifndef NO_BEFOREINSTL
+beforeinstall-headers:
+ @-if [ ! -d $(HEADERDIR) ]; then mkdir $(HEADERDIR); fi
+else
+beforeinstall-headers: .dosomething
+endif
+
+HEADERFILES = libpq++.H
+
+install-headers: beforeinstall-headers
+ @for i in ${HEADERFILES}; do \
+ echo "Installing $(HEADERDIR)/$$i."; \
+ $(INSTALL) -c -m 444 $$i $(HEADERDIR)/$$i; \
+ done
+
+install:: install-headers
+
+include $(MKDIR)/postgres.lib.mk
diff --git a/src/interfaces/libpq++/README b/src/interfaces/libpq++/README
new file mode 100644
index 00000000000..cb5d0aeddb1
--- /dev/null
+++ b/src/interfaces/libpq++/README
@@ -0,0 +1,22 @@
+This directory contains libpq++, the C++ language interface to POSTGRES95.
+libpq++ is implemented on of the libpq library. Users would benefit
+from reading the chapter on libpq in the postgres95 users manual
+before using libpq++.
+
+The initial version of this implementation was done by William Wanders
+(wwanders@sci.kun.nl)
+
+This is only a preliminary attempt at providing something useful for
+people who would like to use C++ to build frontend applications to
+postgres95. The API provided herein is subject to change in later
+versions of postgres95.
+
+For details on how to to use libpq++, see the man page in the man/
+subdirectory and the test programs in the examples/ subdirectory.
+
+libpq++ has been tested with g++, version 2.7.0
+
+- Jolly Chen
+jolly@cs.berkeley.edu
+
+Tue Sep 5 11:09:51 PDT 1995
diff --git a/src/interfaces/libpq++/examples/Makefile b/src/interfaces/libpq++/examples/Makefile
new file mode 100644
index 00000000000..5e51d915876
--- /dev/null
+++ b/src/interfaces/libpq++/examples/Makefile
@@ -0,0 +1,70 @@
+#
+# Makefile for example programs
+#
+
+CPP_PROG = true
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+
+CXXFLAGS+= -I$(HEADERDIR) -I$(srcdir)/libpq -I$(srcdir)/backend \
+ -I$(srcdir)/backend/include
+
+LD_ADD+=-L$(LIBDIR) -lpq++ -lpq
+
+#
+# And where libpq goes, so goes the authentication stuff...
+#
+ifdef KRBVERS
+LD_ADD+= $(KRBLIBS)
+CXXFLAGS+= $(KRBFLAGS)
+endif
+
+P0_PROG:= testlibpq0
+P0_OBJS:= testlibpq0.o
+
+$(P0_PROG): $(addprefix $(objdir)/,$(P0_OBJS))
+ $(CXX) $(CDEBUG) -o $(objdir)/$(@F) $< $(LD_ADD)
+
+P1_PROG:= testlibpq1
+P1_OBJS:= testlibpq1.o
+
+$(P1_PROG): $(addprefix $(objdir)/,$(P1_OBJS))
+ $(CXX) $(CDEBUG) -o $(objdir)/$(@F) $< $(LD_ADD)
+
+P2_PROG:= testlibpq2
+P2_OBJS:= testlibpq2.o
+
+$(P2_PROG): $(addprefix $(objdir)/,$(P2_OBJS))
+ $(CXX) $(CDEBUG) -o $(objdir)/$(@F) $< $(LD_ADD)
+
+P3_PROG:= testlibpq3
+P3_OBJS:= testlibpq3.o
+
+$(P3_PROG): $(addprefix $(objdir)/,$(P3_OBJS))
+ $(CXX) $(CDEBUG) -o $(objdir)/$(@F) $< $(LD_ADD)
+
+P4_PROG:= testlibpq4
+P4_OBJS:= testlibpq4.o
+
+$(P4_PROG): $(addprefix $(objdir)/,$(P4_OBJS))
+ $(CXX) $(CDEBUG) -o $(objdir)/$(@F) $< $(LD_ADD)
+
+P5_PROG:= testlo
+P5_OBJS:= testlo.o
+
+$(P5_PROG): $(addprefix $(objdir)/,$(P5_OBJS))
+ $(CXX) $(CDEBUG) -o $(objdir)/$(@F) $< $(LD_ADD)
+
+OBJS:= $(P0_OBJS) $(P1_OBJS) $(P2_OBJS) $(P3_OBJS) $(P4_OBJS) $(P5_OBJS)
+PROGS:= $(P0_PROG) $(P1_PROG) $(P2_PROG) $(P3_PROG) $(P4_PROG) $(P5_PROG)
+
+CLEANFILES+= $(OBJS) $(PROGS)
+
+all:: $(PROGS)
+
+install:: $(PROGS)
+ @for i in ${PROGS}; do \
+ echo "Installing $$i"; \
+ $(INSTALL) $(objdir)/$$i $(DESTDIR)$(BINDIR)/$$i;\
+ done
diff --git a/src/interfaces/libpq++/examples/testlibpq0.cc b/src/interfaces/libpq++/examples/testlibpq0.cc
new file mode 100644
index 00000000000..76f3ea80712
--- /dev/null
+++ b/src/interfaces/libpq++/examples/testlibpq0.cc
@@ -0,0 +1,49 @@
+/*-------------------------------------------------------------------------
+ *
+ * testlibpq0.c--
+ * small test program for libpq++,
+ * small interactive loop where queries can be entered interactively
+ * and sent to the backend
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq++/examples/Attic/testlibpq0.cc,v 1.1.1.1 1996/07/09 06:22:18 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <stdio.h>
+#include "libpq++.H"
+
+int
+main(int argc, char** argv)
+{
+ ExecStatusType status;
+ PGenv env;
+ PGdatabase* data;
+
+ char buf[10000];
+ int done = 0;
+
+ data = new PGdatabase(&env, "template1");
+
+ if (data->status() == CONNECTION_BAD)
+ printf("connection was unsuccessful\n%s\n", data->errormessage());
+ else
+ printf("connection successful\n");
+
+ while (!done)
+ {
+ printf("> ");fflush(stdout);
+ if (gets(buf) && buf[0]!='\0')
+ if((status = data->exec(buf)) == PGRES_TUPLES_OK)
+ data->printtuples(stdout, 1, "|", 1, 0);
+ else
+ printf("status = %s\nerrorMessage = %s\n", status,
+ data->errormessage());
+ else
+ done = 1;
+ }
+}
diff --git a/src/interfaces/libpq++/examples/testlibpq1.cc b/src/interfaces/libpq++/examples/testlibpq1.cc
new file mode 100644
index 00000000000..1d71f795583
--- /dev/null
+++ b/src/interfaces/libpq++/examples/testlibpq1.cc
@@ -0,0 +1,84 @@
+/*
+ * testlibpq.cc
+ * Test the C++ version of LIBPQ, the POSTGRES frontend library.
+ *
+ * queries the template1 database for a list of database names
+ *
+ */
+#include <stdio.h>
+#include "libpq++.H"
+
+main()
+{
+ char* dbName;
+ int nFields;
+ int i,j;
+
+ /* begin, by creating the parameter environtment for a backend
+ connection. When no parameters are given then the system will
+ try to use reasonable defaults by looking up environment variables
+ or, failing that, using hardwired constants */
+ PGenv env;
+ PGdatabase* data;
+
+ /* Select a database */
+ dbName = "template1";
+
+ /* make a connection to the database */
+ data = new PGdatabase(&env, dbName);
+
+ /* check to see that the backend connection was successfully made */
+ if (data->status() == CONNECTION_BAD) {
+ fprintf(stderr,"Connection to database '%s' failed.\n", dbName);
+ fprintf(stderr,"%s",data->errormessage());
+ delete data;
+ exit(1);
+ }
+
+ /* start a transaction block */
+ if(data->exec("BEGIN") != PGRES_COMMAND_OK) {
+ fprintf(stderr,"BEGIN command failed\n");
+ delete data;
+ exit(1);
+ }
+
+ /* fetch instances from the pg_database, the system catalog of databases*/
+ if (data->exec("DECLARE myportal CURSOR FOR select * from pg_database")
+ != PGRES_COMMAND_OK) {
+ fprintf(stderr,"DECLARE CURSOR command failed\n");
+ delete data;
+ exit(1);
+ }
+
+ if(data->exec("FETCH ALL in myportal") != PGRES_TUPLES_OK) {
+ fprintf(stderr,"FETCH ALL command didn't return tuples properly\n");
+ delete data;
+ exit(1);
+ }
+
+ /* first, print out the attribute names */
+ nFields = data->nfields();
+ for (i=0; i < nFields; i++) {
+ printf("%-15s",data->fieldname(i));
+ }
+ printf("\n\n");
+
+ /* next, print out the instances */
+ for (i=0; i < data->ntuples(); i++) {
+ for (j=0 ; j < nFields; j++) {
+ printf("%-15s", data->getvalue(i,j));
+ }
+ printf("\n");
+ }
+
+ /* close the portal */
+ data->exec("CLOSE myportal");
+
+ /* end the transaction */
+ data->exec("END");
+
+ /* close the connection to the database and cleanup */
+ delete data;
+}
+
+
diff --git a/src/interfaces/libpq++/examples/testlibpq2.cc b/src/interfaces/libpq++/examples/testlibpq2.cc
new file mode 100644
index 00000000000..8eafea00b7a
--- /dev/null
+++ b/src/interfaces/libpq++/examples/testlibpq2.cc
@@ -0,0 +1,71 @@
+/*
+ * testlibpq2.cc
+ * Test of the asynchronous notification interface
+ *
+ populate a database with the following:
+
+CREATE TABLE TBL1 (i int4);
+
+CREATE TABLE TBL2 (i int4);
+
+CREATE RULE r1 AS ON INSERT TO TBL1 DO [INSERT INTO TBL2 values (new.i); NOTIFY TBL2];
+
+ * Then start up this program
+ * After the program has begun, do
+
+INSERT INTO TBL1 values (10);
+
+ *
+ *
+ */
+#include <stdio.h>
+#include "libpq++.H"
+
+main()
+{
+ char* dbName;
+ int nFields;
+ int i,j;
+
+ /* begin, by creating the parameter environtment for a backend
+ connection. When no parameters are given then the system will
+ try to use reasonable defaults by looking up environment variables
+ or, failing that, using hardwired constants */
+ PGenv env;
+ PGdatabase* data;
+ PGnotify* notify;
+
+ dbName = getenv("USER"); /* change this to the name of your test database */
+
+ /* make a connection to the database */
+ data = new PGdatabase(&env, dbName);
+
+ /* check to see that the backend connection was successfully made */
+ if (data->status() == CONNECTION_BAD) {
+ fprintf(stderr,"Connection to database '%s' failed.\n", dbName);
+ fprintf(stderr,"%s",data->errormessage());
+ delete data;
+ exit(1);
+ }
+
+ if (data->exec("LISTEN TBL2") != PGRES_COMMAND_OK) {
+ fprintf(stderr,"LISTEN command failed\n");
+ delete data;
+ exit(1);
+ }
+
+ while (1) {
+ /* check for asynchronous returns */
+ notify = data->notifies();
+ if (notify) {
+ fprintf(stderr,
+ "ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
+ notify->relname, notify->be_pid);
+ free(notify);
+ break;
+ }
+ }
+
+ /* close the connection to the database and cleanup */
+ delete data;
+}
diff --git a/src/interfaces/libpq++/examples/testlibpq2.sql b/src/interfaces/libpq++/examples/testlibpq2.sql
new file mode 100644
index 00000000000..f9c74109328
--- /dev/null
+++ b/src/interfaces/libpq++/examples/testlibpq2.sql
@@ -0,0 +1,5 @@
+CREATE TABLE TBL1 (i int4);
+
+CREATE TABLE TBL2 (i int4);
+
+CREATE RULE r1 AS ON INSERT TO TBL1 DO [INSERT INTO TBL2 values (new.i); NOTIFY TBL2];
diff --git a/src/interfaces/libpq++/examples/testlibpq3.cc b/src/interfaces/libpq++/examples/testlibpq3.cc
new file mode 100644
index 00000000000..1146dffac98
--- /dev/null
+++ b/src/interfaces/libpq++/examples/testlibpq3.cc
@@ -0,0 +1,131 @@
+/*
+ * testlibpq3.cc
+ * Test the C++ version of LIBPQ, the POSTGRES frontend library.
+ * tests the binary cursor interface
+ *
+ *
+ *
+ populate a database by doing the following:
+
+CREATE TABLE test1 (i int4, d float4, p polygon);
+
+INSERT INTO test1 values (1, 3.567, '(3.0, 4.0, 1.0, 2.0)'::polygon);
+
+INSERT INTO test1 values (2, 89.05, '(4.0, 3.0, 2.0, 1.0)'::polygon);
+
+ the expected output is:
+
+tuple 0: got
+ i = (4 bytes) 1,
+ d = (4 bytes) 3.567000,
+ p = (4 bytes) 2 points boundbox = (hi=3.000000/4.000000, lo = 1.000000,2.000000)
+tuple 1: got
+ i = (4 bytes) 2,
+ d = (4 bytes) 89.050003,
+ p = (4 bytes) 2 points boundbox = (hi=4.000000/3.000000, lo = 2.000000,1.000000)
+
+ *
+ */
+#include <stdio.h>
+#include "libpq++.H"
+extern "C" {
+#include "utils/geo-decls.h" /* for the POLYGON type */
+}
+
+main()
+{
+ char* dbName;
+ int nFields;
+ int i,j;
+ int i_fnum, d_fnum, p_fnum;
+
+ /* begin, by creating the parameter environtment for a backend
+ connection. When no parameters are given then the system will
+ try to use reasonable defaults by looking up environment variables
+ or, failing that, using hardwired constants */
+ PGenv env;
+ PGdatabase* data;
+
+ dbName = getenv("USER"); /* change this to the name of your test database */
+
+ /* make a connection to the database */
+ data = new PGdatabase(&env, dbName);
+
+ /* check to see that the backend connection was successfully made */
+ if (data->status() == CONNECTION_BAD) {
+ fprintf(stderr,"Connection to database '%s' failed.\n", dbName);
+ fprintf(stderr,"%s",data->errormessage());
+ delete data;
+ exit(1);
+ }
+
+ /* start a transaction block */
+ if (data->exec("BEGIN") != PGRES_COMMAND_OK) {
+ fprintf(stderr,"BEGIN command failed\n");
+ delete data;
+ exit(1);
+ }
+
+ /* fetch instances from the pg_database, the system catalog of databases*/
+ if (data->exec("DECLARE mycursor BINARY CURSOR FOR select * from test1")
+ != PGRES_COMMAND_OK) {
+ fprintf(stderr,"DECLARE CURSOR command failed\n");
+ delete data;
+ exit(1);
+ }
+
+ if (data->exec("FETCH ALL in mycursor") != PGRES_TUPLES_OK) {
+ fprintf(stderr,"FETCH ALL command didn't return tuples properly\n");
+ delete data;
+ exit(1);
+ }
+
+ i_fnum = data->fieldnum("i");
+ d_fnum = data->fieldnum("d");
+ p_fnum = data->fieldnum("p");
+
+/*
+ for (i=0;i<3;i++) {
+ printf("type[%d] = %d, size[%d] = %d\n",
+ i, data->fieldtype(i),
+ i, data->fieldsize(i));
+ }
+*/
+
+ for (i=0; i < data->ntuples(); i++) {
+ int *ival;
+ float *dval;
+ int plen;
+ POLYGON* pval;
+ /* we hard-wire this to the 3 fields we know about */
+ ival = (int*)data->getvalue(i,i_fnum);
+ dval = (float*)data->getvalue(i,d_fnum);
+ plen = data->getlength(i,p_fnum);
+
+ /* plen doesn't include the length field so need to increment by VARHDSZ*/
+ pval = (POLYGON*) malloc(plen + VARHDRSZ);
+ pval->size = plen;
+ memmove((char*)&pval->npts, data->getvalue(i,p_fnum), plen);
+ printf("tuple %d: got\n", i);
+ printf(" i = (%d bytes) %d,\n",
+ data->getlength(i,i_fnum), *ival);
+ printf(" d = (%d bytes) %f,\n",
+ data->getlength(i,d_fnum), *dval);
+ printf(" p = (%d bytes) %d points \tboundbox = (hi=%f/%f, lo = %f,%f)\n",
+ data->getlength(i,d_fnum),
+ pval->npts,
+ pval->boundbox.xh,
+ pval->boundbox.yh,
+ pval->boundbox.xl,
+ pval->boundbox.yl);
+ }
+
+ /* close the portal */
+ data->exec("CLOSE mycursor");
+
+ /* end the transaction */
+ data->exec("END");
+
+ /* close the connection to the database and cleanup */
+ delete data;
+}
diff --git a/src/interfaces/libpq++/examples/testlibpq3.sql b/src/interfaces/libpq++/examples/testlibpq3.sql
new file mode 100644
index 00000000000..f024c0b071b
--- /dev/null
+++ b/src/interfaces/libpq++/examples/testlibpq3.sql
@@ -0,0 +1,6 @@
+CREATE TABLE test1 (i int4, d float4, p polygon);
+
+INSERT INTO test1 values (1, 3.567, '(3.0, 4.0, 1.0, 2.0)'::polygon);
+
+INSERT INTO test1 values (2, 89.05, '(4.0, 3.0, 2.0, 1.0)'::polygon);
+
diff --git a/src/interfaces/libpq++/examples/testlibpq4.cc b/src/interfaces/libpq++/examples/testlibpq4.cc
new file mode 100644
index 00000000000..9d5ca3ec769
--- /dev/null
+++ b/src/interfaces/libpq++/examples/testlibpq4.cc
@@ -0,0 +1,69 @@
+/*
+ * testlibpq4.cc
+ * Test the C++ version of LIBPQ, the POSTGRES frontend library.
+ * tests the copy in features
+ *
+ */
+#include <stdio.h>
+#include "libpq++.H"
+
+#define DEBUG printf("Got here %d\n", __LINE__);
+main()
+{
+ char* dbName;
+ int nFields;
+ int i,j;
+
+ /* begin, by creating the parameter environment for a backend
+ connection. When no parameters are given then the system will
+ try to use reasonable defaults by looking up environment variables
+ or, failing that, using hardwired constants */
+ PGenv env;
+ PGdatabase* data;
+
+ dbName = getenv("USER"); /* change this to the name of your test database */
+
+ /* make a connection to the database */
+ data = new PGdatabase(&env, dbName);
+
+ /* check to see that the backend connection was successfully made */
+ if (data->status() == CONNECTION_BAD) {
+ fprintf(stderr,"Connection to database '%s' failed.\n", dbName);
+ fprintf(stderr,"%s",data->errormessage());
+ delete data;
+ exit(1);
+ }
+
+ /* start a transaction block */
+ if(data->exec("BEGIN") != PGRES_COMMAND_OK) {
+ fprintf(stderr,"BEGIN command failed\n");
+ delete data;
+ exit(1);
+ }
+
+ if (data->exec("CREATE TABLE foo (a int4, b char16, d float8)") !=
+ PGRES_COMMAND_OK) {
+ fprintf(stderr,"CREATE TABLE foo command failed\n");
+ delete data;
+ exit(1);
+ }
+
+ if (data->exec("COPY foo FROM STDIN") != PGRES_COMMAND_OK) {
+ fprintf(stderr,"COPY foo FROM STDIN\n");
+ delete data;
+ exit(1);
+ }
+
+ data->putline("3\thello world\t4.5\n");
+ data->putline("4\tgoodbye word\t7.11\n");
+ data->putline(".\n");
+ data->endcopy();
+ data->exec("SELECT * FROM foo");
+ data->printtuples(stdout,1,"|",1,0);
+ data->exec("DROP TABLE foo");
+ // end the transaction
+ data->exec("END");
+
+ // close the connection to the database and cleanup
+ delete data;
+}
diff --git a/src/interfaces/libpq++/examples/testlo.cc b/src/interfaces/libpq++/examples/testlo.cc
new file mode 100644
index 00000000000..be3459794f9
--- /dev/null
+++ b/src/interfaces/libpq++/examples/testlo.cc
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * lotest.cc--
+ * test using large objects with libpq
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq++/examples/Attic/testlo.cc,v 1.1.1.1 1996/07/09 06:22:19 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include "libpq++.H"
+extern "C" {
+#include "libpq/libpq-fs.h"
+}
+
+int
+main(int argc, char **argv)
+{
+ char *in_filename, *out_filename;
+ char *database;
+ Oid lobjOid;
+ PGenv env;
+ PGlobj *object;
+
+ if (argc < 4 || argc > 5) {
+ fprintf(stderr, "Usage: %s database_name in_filename out_filename [oid]\n",
+ argv[0]);
+ exit(1);
+ }
+
+ database = argv[1];
+ in_filename = argv[2];
+ out_filename = argv[3];
+
+ /*
+ * set up the connection and create a largeobject for us
+ */
+ if (argc == 4) {
+ object = new PGlobj(&env, database);
+ } else {
+ object = new PGlobj(&env, database, atoi(argv[4]));
+ }
+
+ /* check to see that the backend connection was successfully made */
+ if (object->status() == CONNECTION_BAD) {
+ fprintf(stderr,"Connection to database '%s' failed.\n", database);
+ fprintf(stderr,"%s",object->errormessage());
+ delete object;
+ exit(1);
+ }
+
+ object->exec("BEGIN");
+ printf("importing file \"%s\" ...\n", in_filename);
+ object->import(in_filename);
+ printf("exporting large object to file \"%s\" ...\n", out_filename);
+ object->export(out_filename);
+ object->exec("END"); // WHY DOES IT CORE DUMP HERE ???
+ delete object;
+}
diff --git a/src/interfaces/libpq++/libpq++.H b/src/interfaces/libpq++/libpq++.H
new file mode 100644
index 00000000000..9b3e1739007
--- /dev/null
+++ b/src/interfaces/libpq++/libpq++.H
@@ -0,0 +1,173 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpq++.H
+ *
+ *
+ * DESCRIPTION
+ * C++ client interface to Postgres
+ * used for building front-end applications
+ *
+ * NOTES
+ * Currently under construction.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *
+ * $Id: libpq++.H,v 1.1.1.1 1996/07/09 06:22:18 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef LIBPQXX_H
+#define LIBPQXX_H
+
+#include <stdio.h>
+#include <strings.h>
+
+extern "C" {
+#include "libpq-fe.h"
+#include "fe-auth.h"
+}
+
+// ****************************************************************
+//
+// PGenv - the environment for setting up a connection to postgres
+//
+// ****************************************************************
+class PGenv {
+ friend class PGconnection;
+ char* pgauth;
+ char* pghost;
+ char* pgport;
+ char* pgoption;
+ char* pgtty;
+public:
+ PGenv(); // default ctor will use reasonable defaults
+ // will use environment variables PGHOST, PGPORT,
+ // PGOPTION, PGTTY
+ PGenv(char* auth, char* host, char* port, char* option, char* tty);
+ void setValues(char* auth, char* host, char* port, char* option, char* tty);
+ ~PGenv();
+};
+
+// ****************************************************************
+//
+// PGconnection - a connection made to a postgres backend
+//
+// ****************************************************************
+class PGconnection {
+ friend class PGdatabase;
+ friend class PGlobj;
+ PGenv* env;
+ PGconn* conn;
+ PGresult* result;
+
+ char errorMessage[ERROR_MSG_LENGTH];
+public:
+ PGconnection(); // use reasonable defaults
+ PGconnection(PGenv* env, char* dbName); // connect to the database with
+ // given environment and database name
+ ConnStatusType status();
+ char* errormessage() {return errorMessage;};
+
+ // returns the database name of the connection
+ char* dbName() {return PQdb(conn);};
+
+ ExecStatusType exec(char* query); // send a query to the backend
+ PGnotify* notifies() {exec(" "); return PQnotifies(conn);};
+ ~PGconnection(); // close connection and clean up
+protected:
+ ConnStatusType connect(PGenv* env, char* dbName);
+};
+
+// ****************************************************************
+//
+// PGdatabase - a class for accessing databases
+//
+// ****************************************************************
+class PGdatabase : public PGconnection {
+public:
+ PGdatabase() : PGconnection() {}; // use reasonable defaults
+ // connect to the database with
+ PGdatabase(PGenv* env, char* dbName) : PGconnection(env, dbName) {};
+ // query result access
+ int ntuples()
+ {return PQntuples(result);};
+ int nfields()
+ {return PQnfields(result);};
+ char* fieldname(int field_num)
+ {return PQfname(result, field_num);};
+ int fieldnum(char* field_name)
+ {return PQfnumber(result, field_name);};
+ Oid fieldtype(int field_num)
+ {return PQftype(result, field_num);};
+ Oid fieldtype(char* field_name)
+ {return PQftype(result, fieldnum(field_name));};
+ int2 fieldsize(int field_num)
+ {return PQfsize(result, field_num);};
+ int2 fieldsize(char* field_name)
+ {return PQfsize(result, fieldnum(field_name));};
+ char* getvalue(int tup_num, int field_num)
+ {return PQgetvalue(result, tup_num, field_num);};
+ char* getvalue(int tup_num, char* field_name)
+ {return PQgetvalue(result, tup_num, fieldnum(field_name));};
+ int getlength(int tup_num, int field_num)
+ {return PQgetlength(result, tup_num, field_num);};
+ int getlength(int tup_num, char* field_name)
+ {return PQgetlength(result, tup_num, fieldnum(field_name));};
+ void printtuples(FILE *out, int fillAlign, char *fieldSep,
+ int printHeader, int quiet)
+ {PQdisplayTuples(result, out, fillAlign, fieldSep, printHeader, quiet);};
+ // copy command related access
+ int getline(char* string, int length)
+ {return PQgetline(conn, string, length);};
+ void putline(char* string)
+ {PQputline(conn, string);};
+ int endcopy()
+ {return PQendcopy(conn);};
+ ~PGdatabase() {}; // close connection and clean up
+};
+
+// ****************************************************************
+//
+// PGlobj - a class for accessing Large Object in a database
+//
+// ****************************************************************
+class PGlobj : public PGconnection {
+ int fd;
+ Oid object;
+public:
+ PGlobj(); // use reasonable defaults and create large object
+ PGlobj(Oid lobjId); // use reasonable defaults and open large object
+ PGlobj(PGenv* env, char* dbName); // create large object
+ PGlobj(PGenv* env, char* dbName, Oid lobjId); // open large object
+ int read(char* buf, int len)
+ {return lo_read(conn, fd, buf, len);};
+ int write(char* buf, int len)
+ {return lo_write(conn, fd, buf, len);};
+ int lseek(int offset, int whence)
+ {return lo_lseek(conn, fd, offset, whence);};
+ int tell()
+ {return lo_tell(conn, fd);};
+ int unlink();
+ int import(char* filename);
+ int export(char* filename);
+ ~PGlobj(); // close connection and clean up
+};
+
+//
+// these are the environment variables used for getting defaults
+//
+
+#define ENV_DEFAULT_AUTH "PGAUTH"
+#define ENV_DEFAULT_DBASE "PGDATABASE"
+#define ENV_DEFAULT_HOST "PGHOST"
+#define ENV_DEFAULT_OPTION "PGOPTION"
+#define ENV_DEFAULT_PORT "PGPORT"
+#define ENV_DEFAULT_TTY "PGTTY"
+
+// buffer size
+#define BUFSIZE 1024
+
+#endif /* LIBPQXX_H */
diff --git a/src/interfaces/libpq++/man/libpq++.3 b/src/interfaces/libpq++/man/libpq++.3
new file mode 100644
index 00000000000..ebca7ab7bb1
--- /dev/null
+++ b/src/interfaces/libpq++/man/libpq++.3
@@ -0,0 +1,434 @@
+.\"
+.\" POSTGRES95 Data Base Management System
+.\"
+.\" Copyright (c) 1994-5 Regents of the University of California
+.\"
+.\" POSTGRES Data Base Management System
+.\" Copyright (c) 1988,1994 Regents of the University of California
+.\"
+.\" Permission to use, copy, modify, and distribute this software and its
+.\" documentation for any purpose, without fee, and without a written agreement
+.\" is hereby granted, provided that the above copyright notice and this
+.\" paragraph and the following two paragraphs appear in all copies.
+.\"
+.\" IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+.\" DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+.\" LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
+.\" DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+.\" ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
+.\" PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+.\"
+.\"
+.\" $Id: libpq++.3,v 1.1.1.1 1996/07/09 06:22:19 scrappy Exp $
+.\"
+.\" ------------------------------------------------------------------
+.\" .(l, .)l
+.\" fake "-me"-style lists
+.de (l
+.nf
+.ie '\\$1'M' .in +0n
+.el .in +5n
+..
+.de )l
+.fi
+.in
+..
+.\" .(C, .)C
+.\" constant-width font blocks
+.de (C
+.ft C
+.(b
+.(l \\$1
+.sp
+..
+.de )C
+.sp
+.)l
+.)b
+.ft R
+..
+.\" ------------------------------------------------------------------
+.de SE
+.nr si 0
+.nr so 0
+.nr $0 0
+.nr $i \\n(si*\\n($0
+.in \\n($i+\\n(po
+..
+.\" ------------------------------------------------------------------
+.de SP
+.he '\fB\\$1 (\\$2)'\\$3'\\$1 (\\$2)\fR'
+..
+.\" ------------------------------------------------------------------
+.de SS
+.PP
+.B \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8
+.PP
+..
+.\" ------------------------------------------------------------------
+.SB
+.ds II \s-1INGRES\s0
+.ds PG \s-1POSTGRES95\s0
+.ds UU \s-1UNIX\s0
+.ds PQ \s-1POSTQUEL\s0
+.ds LI \s-1LIBPQ++\s0
+.ds PV 4.2
+.SB
+.TH INTRODUCTION LIBPQ++ 07/24/95
+.XA 0 "Libpq++"
+.BH "LIBPQ++"
+.SH DESCRIPTION
+\*(LI is the C++ API to \*(PG. \*(LI is a set of classes which allow
+client programs to connect to the \*(PG backend server. These connections
+come in two forms: a Database Class and a Large Object class.
+.PP
+The Database Class is intended for manipulating a database. You can
+send all sorts of SQL queries to the \*(PG backend server and retrieve
+the responses of the server.
+.PP
+The Large Object Class is intended for manipulating a large object
+in a database. Although a Large Object instance can send normal
+queries to the \*(PG backend server it is only intended for simple
+queries that do not return any data. A large object should be seen
+as a file stream. In future it should behave much like the C++ file
+streams
+.IR cin ,
+.IR cout
+and
+.IR cerr .
+This version of the documentation is based on the C library. Three
+short programs are listed at the end of this section as examples of
+\*(LI programming (though not necessarily of good programming).
+.PP
+There are several examples of \*(LI applications in the following
+directory:
+.(C
+\&.../src/libpq++/examples
+.)C
+.XA 1 "Control and Initialization"
+.SH "CONTROL AND INITIALIZATION"
+.XA 2 "Environment Variables"
+.SS "Environment Variables"
+The following environment variables can be used to set up default
+values for an environment and to avoid hard-coding database names into
+an application program:
+.TP 15n
+.BR PGDATABASE
+sets the default \*(PG database name.
+.TP 15n
+.BR PGHOST
+sets the default server name.
+.TP 15n
+.BR PGOPTIONS
+sets additional runtime options for the \*(PG backend.
+.TP 15n
+.BR PGPORT
+sets the default communication port with the \*(PG backend.
+.TP 15n
+.BR PGTTY
+sets the file or tty on which debugging messages from the backend server
+are displayed.
+.TP 15n
+.BR PGREALM
+sets the
+.IR Kerberos
+realm to use with \*(PG, if it is different from the local realm. If
+.SM PGREALM
+is set, \*(PG applications will attempt authentication with servers
+for this realm and use separate ticket files to avoid conflicts with
+local ticket files. This environment variable is only used if
+.IR Kerberos
+authentication is enabled.
+.TP 15n
+.BR PGAUTH
+sets the type of authentication which should be used. Currently
+only
+.IR unauth ,
+.IR krb4 ,
+and
+.IR krb5 .
+are supported. Depending on whether you compiled in support for those.
+.XA 1 "Database Connection Functions"
+.SH "DATABASE ENVIRONMENT CLASS: PGenv"
+The database environment class provides C++ objects for manipulating the
+above environment variables.
+.TP 15n
+.BR PGenv
+Create an environment for the running program.
+.(C
+PGenv()
+PGenv(char* auth, char* host, char* port, char* option, char* tty)
+.)C
+The first form of this object's constructor sets up the defaults for
+the program from the environment variables listed above.
+The second allows the programmer to hardcode the values into the program.
+The values of the second form relate directly to the environment variables
+above.
+.SH "DATABASE CLASS: PGdatabase"
+The database class is a provides C++ objects that have a connection
+to a backend server. To create such an object one first need
+the apropriate environment for the backend to access.
+The following constructors deal with making a connection to a backend
+server from a C++ program.
+.TP 15n
+.BR PGdatabase
+Make a new connection to a backend database server.
+.(C
+PGdatabase(PGenv *env, char *dbName);
+.)C
+After a PGdatabase has been created it should be checked to make sure
+the connection to the database succeded before sending
+queries to the object. This can easily be done by
+retrieving the current status of the PGdatabase object with the
+.IR status
+command.
+.BR PGdatabase::status
+Returns the status of the PGdatabase object.
+
+.(C
+ConnStatus PGdatabase::status()
+.)C
+
+the following values are allowed
+
+.(C
+CONNECTION_OK
+CONNECTION_BAD
+.)C
+
+.XA 1 "Query Execution Functions"
+.SH "QUERY EXECUTION FUNCTIONS"
+.TP 15n
+.BR PGdatabase::exec
+Submits a query to \*(PG and returns result status. In case of an error
+.IR PGdatabase::errormessage
+can be used to get more information on the error.
+.(C
+void
+ExecStatusType PGdatabase::exec(char *query);
+.)C
+The following status results can be expected.
+.(C
+PGRES_EMPTY_QUERY,
+PGRES_COMMAND_OK, /* the query was a command */
+PGRES_TUPLES_OK, /* the query successfully returned tuples */
+PGRES_COPY_OUT,
+PGRES_COPY_IN,
+PGRES_BAD_RESPONSE, /* an unexpected response was received */
+PGRES_NONFATAL_ERROR,
+PGRES_FATAL_ERROR
+.)C
+.IP
+If the result status is PGRES_TUPLES_OK, then the following routines can
+be used to retrieve the tuples returned by the query.
+.IP
+.BR PGdatabase::ntuples
+returns the number of tuples (instances) in the query result.
+.(C
+int PGdatabase::ntuples();
+.)C
+.BR PGdatabase::nfields
+returns the number of fields (attributes) in the query result.
+.(C
+int PGdatabase::nfields();
+.)C
+.BR PGdatabase::fieldname
+returns the field (attribute) name associated with the given field index.
+Field indices start at 0.
+.(C
+char* PGdatabase::fieldname(int field_index);
+.)C
+.BR PGdatabase::fieldnum
+returns the field (attribute) index associated with the given field name.
+.(C
+int PGdatabase::fieldnum(char* field_name);
+.)C
+.BR PGdatabase::fieldtype
+returns the field type of associated with the given field index or name.
+The integer returned is an internal coding of the type. Field indices start
+at 0.
+.(C
+Oid PGdatabase::fieldtype(int field_index);
+Oid PGdatabase::fieldtype(char* field_name);
+.)C
+.BR PGdatabase::fieldsize
+returns the size in bytes of the field associated with the given field
+index or name. If the size returned is -1, the field is a variable length
+field. Field indices start at 0.
+.(C
+int2 PGdatabase::fieldsize(int field_index);
+int2 PGdatabase::fieldsize(char* field_name);
+.)C
+.BR PGdatabase::getvalue
+returns the field (attribute) value. For most queries, the values
+returned by
+.IR PGdatabase::getvalue
+is a null-terminated ASCII string representation
+of the attribute value. If the query was a result of a
+.BR BINARY
+cursor, then the values returned by
+.IR PGdatabase::getvalue
+is the binary representation of the type in the internal format of the
+backend server. It is the programmer's responsibility to cast and
+convert the data to the correct C++ type. The value return by
+.IR PGdatabase::getvalue
+points to storage that is part of the PGdatabase structure. One must
+explicitly copy the value into other storage if it is to be used past
+the next query.
+.(C
+char* PGdatabase::getvalue(int tup_num, int field_index);
+char* PGdatabase::getvalue(int tup_num, char* field_name);
+.)C
+.BR PGdatabase::getlength
+returns the length of a field (attribute) in bytes. If the field
+is a
+.IR "struct varlena" ,
+the length returned here does
+.BR not
+include the size field of the varlena, i.e., it is 4 bytes less.
+.(C
+int PGdatabase::getlength(int tup_num, int field_index);
+int PGdatabase::getlength(int tup_num, char* field_name);
+.)C
+.BR PGdatabase::printtuples
+prints out all the tuples and, optionally, the attribute names to the
+specified output stream.
+.(C
+void PGdatabase::printtuples(
+ FILE* fout, /* output stream */
+ int printAttName,/* print attribute names or not*/
+ int terseOutput, /* delimiter bars or not?*/
+ int width /* width of column, variable width if 0*/
+ );
+.)C
+.XA 1 "Asynchronous Notification"
+.SH "ASYNCHRONOUS NOTIFICATION"
+\*(PG supports asynchronous notification via the
+.IR LISTEN
+and
+.IR NOTIFY
+commands. A backend registers its interest in a particular relation
+with the LISTEN command. All backends that are listening on a
+particular relation will be notified asynchronously when a NOTIFY of
+that relation name is executed by another backend. No additional
+information is passed from the notifier to the listener. Thus,
+typically, any actual data that needs to be communicated is transferred
+through the relation.
+.PP
+\*(LI applications are notified whenever a connected backend has
+received an asynchronous notification. However, the communication from
+the backend to the frontend is not asynchronous. The \*(LI application
+must poll the backend to see if there is any pending notification
+information. After the execution of a query, a frontend may call
+.IR PGdatabase::notifies
+to see if any notification data is currently available from the backend.
+.TP 15n
+.BR PGdatabase::notifies
+returns the notification from a list of unhandled notifications from the
+backend. Returns NULL if there is no pending notifications from the
+backend.
+.IR PGdatabase::notifies
+behaves like the popping of a stack. Once a notification is returned
+from
+.IR PGdatabase::notifies,
+it is considered handled and will be removed from the list of
+notifications.
+.(C
+PGnotify* PGdatabase::notifies()
+.)C
+.PP
+The second sample program gives an example of the use of asynchronous
+notification.
+.XA 1 "Functions Associated with the COPY Command"
+.SH "FUNCTIONS ASSOCIATED WITH THE COPY COMMAND"
+The
+.IR copy
+command in \*(PG has options to read from or write to the network
+connection used by \*(LI. Therefore, functions are necessary to
+access this network connection directly so applications may take full
+advantage of this capability.
+.TP 15n
+.BR PGdatabase::getline
+Reads a newline-terminated line of characters (transmitted by the
+backend server) into a buffer
+.IR string
+of size
+.IR length .
+Like
+.IR fgets (3),
+this routine copies up to
+.IR length "-1"
+characters into
+.IR string .
+It is like
+.IR gets (3),
+however, in that it converts the terminating newline into a null
+character.
+.IP
+.IR PGdatabase::getline
+returns EOF at EOF, 0 if the entire line has been read, and 1 if the
+buffer is full but the terminating newline has not yet been read.
+.IP
+Notice that the application must check to see if a new line consists
+of the single character \*(lq.\*(rq, which indicates that the backend
+server has finished sending the results of the
+.IR copy
+command. Therefore, if the application ever expects to receive lines
+that are more than
+.IR length "-1"
+characters long, the application must be sure to check the return
+value of
+.IR PGdatabase::getline
+very carefully.
+.IP
+.(C
+int PGdatabase::getline(char* string, int length)
+.)C
+.TP 15n
+.BR PGdatabase::putline
+Sends a null-terminated
+.IR string
+to the backend server.
+.IP
+The application must explicitly send the single character \*(lq.\*(rq
+to indicate to the backend that it has finished sending its data.
+.(C
+void PGdatabase::putline(char* string)
+.)C
+.TP 15n
+.BR PGdatabase::endcopy
+Syncs with the backend. This function waits until the backend has
+finished processing the copy. It should either be issued when the
+last string has been sent to the backend using
+.IR PGdatabase::putline
+or when the last string has been received from the backend using
+.IR PGdatabase::getline .
+It must be issued or the backend may get \*(lqout of sync\*(rq with
+the frontend. Upon return from this function, the backend is ready to
+receive the next query.
+.IP
+The return value is 0 on successful completion, nonzero otherwise.
+.(C
+int PGdatabase::endcopy()
+.)C
+As an example:
+.(C
+PGdatabase data;
+data.exec("create table foo (a int4, b char16, d float8)");
+data.exec("copy foo from stdin");
+data.putline("3\etHello World\et4.5\en");
+data.putline("4\etGoodbye World\et7.11\en");
+\&...
+data.putline(".\en");
+data.endcopy();
+.)C
+.SH BUGS
+The query buffer is 8192 bytes long, and queries over that length will
+be silently truncated.
+.bp
+The PGlobj class is largely untested. Use with caution.
diff --git a/src/interfaces/libpq++/pgconnection.cc b/src/interfaces/libpq++/pgconnection.cc
new file mode 100644
index 00000000000..777a12e8e7a
--- /dev/null
+++ b/src/interfaces/libpq++/pgconnection.cc
@@ -0,0 +1,94 @@
+/*-------------------------------------------------------------------------
+ *
+ * FILE
+ * pgconnection.cc
+ *
+ * DESCRIPTION
+ * implementation of the PGconnection class.
+ * PGconnection encapsulates a frontend to backend connection
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq++/Attic/pgconnection.cc,v 1.1.1.1 1996/07/09 06:22:18 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "libpq++.H"
+
+// default constructor
+// checks environment variable for database name
+PGconnection::PGconnection()
+{
+ char* name;
+ PGenv* newenv;
+
+ conn = NULL;
+ result = NULL;
+ errorMessage[0] = '\0';
+
+ newenv = new PGenv(); // use reasonable defaults for the environment
+ if (!(name = getenv(ENV_DEFAULT_DBASE)))
+ return;
+ connect(newenv, name);
+}
+
+// constructor -- for given environment and database name
+PGconnection::PGconnection(PGenv* env, char* dbName)
+{
+ conn = NULL;
+ result = NULL;
+ errorMessage[0] = '\0';
+ connect(env, dbName);
+}
+
+// destructor - closes down the connection and cleanup
+PGconnection::~PGconnection()
+{
+ if (result) PQclear(result);
+ if (conn) PQfinish(conn);
+}
+
+// PGconnection::connect
+// establish a connection to a backend
+ConnStatusType
+PGconnection::connect(PGenv* newenv, char* dbName)
+{
+#if 0
+ FILE *debug;
+ debug = fopen("/tmp/trace.out","w");
+ PQtrace(conn, debug);
+#endif
+
+ env = newenv;
+ fe_setauthsvc(env->pgauth, errorMessage);
+ conn = PQsetdb(env->pghost, env->pgport, env->pgoption, env->pgtty, dbName);
+ if(strlen(errorMessage))
+ return CONNECTION_BAD;
+ else
+ return status();
+}
+
+// PGconnection::status -- return connection or result status
+ConnStatusType
+PGconnection::status()
+{
+ return PQstatus(conn);
+}
+
+// PGconnection::exec -- send a query to the backend
+ExecStatusType
+PGconnection::exec(char* query)
+{
+ if (result)
+ PQclear(result);
+
+ result = PQexec(conn, query);
+ if (result)
+ return PQresultStatus(result);
+ else {
+ strcpy(errorMessage, PQerrorMessage(conn));
+ return PGRES_FATAL_ERROR;
+ }
+}
diff --git a/src/interfaces/libpq++/pgenv.cc b/src/interfaces/libpq++/pgenv.cc
new file mode 100644
index 00000000000..aab4f213eb2
--- /dev/null
+++ b/src/interfaces/libpq++/pgenv.cc
@@ -0,0 +1,109 @@
+/*-------------------------------------------------------------------------
+ *
+ * FILE
+ * PGenv.cc
+ *
+ * DESCRIPTION
+ * PGenv is the environment for setting up a connection to a
+ * postgres backend, captures the host, port, tty, options and
+ * authentication type.
+ *
+ * NOTES
+ * Currently under construction.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq++/Attic/pgenv.cc,v 1.1.1.1 1996/07/09 06:22:18 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <stdlib.h>
+#include "libpq++.H"
+
+#define DefaultAuth DEFAULT_CLIENT_AUTHSVC
+#define DefaultPort POSTPORT
+
+// default constructor for PGenv
+// checks the environment variables
+PGenv::PGenv()
+{
+ char* temp;
+
+ pgauth = NULL;
+ pghost = NULL;
+ pgport = NULL;
+ pgoption = NULL;
+ pgtty = NULL;
+
+ setValues(getenv(ENV_DEFAULT_AUTH), getenv(ENV_DEFAULT_HOST),
+ getenv(ENV_DEFAULT_PORT), getenv(ENV_DEFAULT_OPTION),
+ getenv(ENV_DEFAULT_TTY));
+}
+
+// constructor for given environment
+PGenv::PGenv(char* auth, char* host, char* port, char* option, char* tty)
+{
+ pgauth = NULL;
+ pghost = NULL;
+ pgport = NULL;
+ pgoption = NULL;
+ pgtty = NULL;
+
+ setValues(auth, host, port, option, tty);
+}
+
+// allocate memory and set internal structures to match
+// required environment
+void
+PGenv::setValues(char* auth, char* host, char* port, char* option, char* tty)
+{
+ char* temp;
+
+ temp = (auth) ? auth : DefaultAuth;
+
+ if (pgauth)
+ free(pgauth);
+ pgauth = strdup(temp);
+
+ temp = (host) ? host : DefaultHost;
+
+ if (pghost)
+ free(pghost);
+ pghost = strdup(temp);
+
+ temp = (port) ? port : DefaultPort;
+
+ if (pgport)
+ free(pgport);
+ pgport = strdup(temp);
+
+ temp = (option) ? option : DefaultOption;
+
+ if (pgoption)
+ free(pgoption);
+ pgoption = strdup(temp);
+
+ temp = (tty) ? tty : DefaultTty;
+
+ if (pgtty)
+ free(pgtty);
+ pgtty = strdup(temp);
+}
+
+// default destrutor
+// frees allocated memory for internal structures
+PGenv::~PGenv()
+{
+ if (pgauth)
+ free(pgauth);
+ if (pghost)
+ free(pghost);
+ if (pgport)
+ free(pgport);
+ if (pgoption)
+ free(pgoption);
+ if (pgtty)
+ free(pgtty);
+}
diff --git a/src/interfaces/libpq++/pglobject.cc b/src/interfaces/libpq++/pglobject.cc
new file mode 100644
index 00000000000..20c7ad5e22d
--- /dev/null
+++ b/src/interfaces/libpq++/pglobject.cc
@@ -0,0 +1,152 @@
+
+/*-------------------------------------------------------------------------
+ *
+ * FILE
+ * pglobject.cc
+ *
+ * DESCRIPTION
+ * implementation of the PGlobj class.
+ * PGlobj encapsulates a frontend to backend connection
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq++/Attic/pglobject.cc,v 1.1.1.1 1996/07/09 06:22:18 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "libpq++.H"
+
+extern "C" {
+#include "libpq/libpq-fs.h"
+}
+
+// default constructor
+// creates a large object in the default database
+PGlobj::PGlobj() : PGconnection::PGconnection() {
+ object = lo_creat(conn, INV_READ|INV_WRITE);
+ if (object == 0) {
+ sprintf(errorMessage, "PGlobj: can't create large object");
+ }
+ fd = lo_open(conn, object, INV_READ|INV_WRITE);
+ if (fd < 0) {
+ sprintf(errorMessage, "PGlobj: can't open large object %d", object);
+ } else
+ sprintf(errorMessage, "PGlobj: created and opened large object %d",
+ object);
+
+}
+
+// constructor
+// open an existing large object in the default database
+PGlobj::PGlobj(Oid lobjId) : PGconnection::PGconnection() {
+ object = lobjId;
+ fd = lo_open(conn, object, INV_READ|INV_WRITE);
+ if (fd < 0) {
+ sprintf(errorMessage, "PGlobj: can't open large object %d", object);
+ } else
+ sprintf(errorMessage, "PGlobj: opened large object %d",
+ object);
+}
+
+// constructor
+// create a large object in the given database
+PGlobj::PGlobj(PGenv* env, char* dbName) : PGconnection::PGconnection(env,dbName) {
+ object = lo_creat(conn, INV_READ|INV_WRITE);
+ if (object == 0) {
+ sprintf(errorMessage, "PGlobj: can't create large object");
+ }
+ fd = lo_open(conn, object, INV_READ|INV_WRITE);
+ if (fd < 0) {
+ sprintf(errorMessage, "PGlobj: can't open large object %d", object);
+ } else
+ sprintf(errorMessage, "PGlobj: created and opened large object %d",
+ object);
+}
+
+// constructor
+// open an existing large object in the given database
+PGlobj::PGlobj(PGenv* env, char* dbName, Oid lobjId) : PGconnection::PGconnection(env,dbName) {
+ object = lobjId;
+ fd = lo_open(conn, object, INV_READ|INV_WRITE);
+ if (fd < 0) {
+ sprintf(errorMessage, "PGlobj: can't open large object %d", object);
+ } else
+ sprintf(errorMessage, "PGlobj: created and opened large object %d",
+ object);
+}
+
+// PGlobj::unlink
+// destruct large object and delete from it from the database
+int
+PGlobj::unlink() {
+ int temp = lo_unlink(conn, object);
+ if (temp) {
+ return temp;
+ } else {
+ delete this;
+ return temp;
+ }
+}
+
+// PGlobj::import -- import a given file into the large object
+int
+PGlobj::import(char* filename) {
+ char buf[BUFSIZE];
+ int nbytes, tmp;
+ int in_fd;
+
+ // open the file to be read in
+ in_fd = open(filename, O_RDONLY, 0666);
+ if (in_fd < 0) { /* error */
+ sprintf(errorMessage, "PGlobj::import: can't open unix file\"%s\"", filename);
+ return -1;
+ }
+
+ // read in from the Unix file and write to the inversion file
+ while ((nbytes = ::read(in_fd, buf, BUFSIZE)) > 0) {
+ tmp = lo_write(conn, fd, buf, nbytes);
+ if (tmp < nbytes) {
+ sprintf(errorMessage, "PGlobj::import: error while reading \"%s\"",
+ filename);
+ return -1;
+ }
+ }
+
+ (void) close(in_fd);
+ return 0;
+}
+
+// PGlobj::export -- export large object to given file
+int
+PGlobj::export(char* filename) {
+ int out_fd;
+ char buf[BUFSIZE];
+ int nbytes, tmp;
+
+ // open the file to be written to
+ out_fd = open(filename, O_CREAT|O_WRONLY, 0666);
+ if (out_fd < 0) { /* error */
+ sprintf(errorMessage, "PGlobj::export: can't open unix file\"%s\"",
+ filename);
+ return -1;
+ }
+
+ // read in from the Unix file and write to the inversion file
+ while ((nbytes = lo_read(conn, fd, buf, BUFSIZE)) > 0) {
+ tmp = ::write(out_fd, buf, nbytes);
+ if (tmp < nbytes) {
+ sprintf(errorMessage,"PGlobj::export: error while writing \"%s\"",
+ filename);
+ return -1;
+ }
+ }
+ (void) close(out_fd);
+ return 0;
+}
+
+// default destructor -- closes large object
+PGlobj::~PGlobj() {
+ if (fd >= 0)
+ lo_close(conn, fd);
+}
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
new file mode 100644
index 00000000000..dc6318340ea
--- /dev/null
+++ b/src/interfaces/libpq/Makefile
@@ -0,0 +1,98 @@
+#-------------------------------------------------------------------------
+#
+# Makefile
+# Makefile for libpq library
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+# $Header: /cvsroot/pgsql/src/interfaces/libpq/Makefile,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $
+#
+#-------------------------------------------------------------------------
+
+LIB= pq
+
+MKDIR= ../mk
+include $(MKDIR)/postgres.mk
+
+CFLAGS+= -I$(srcdir)/backend/include \
+ -I$(srcdir)/backend \
+ -I$(CURDIR) \
+
+ifdef KRBVERS
+CFLAGS+= $(KRBFLAGS)
+endif
+
+# dllist.c is found in backend/lib
+VPATH:= $(VPATH):$(srcdir)/backend/lib
+
+LIBSRCS= fe-auth.c fe-connect.c fe-exec.c fe-misc.c fe-lobj.c \
+ dllist.c pqsignal.c
+ifeq ($(PORTNAME), next)
+VPATH:=$(VPATH):$(srcdir)/backend/port/$(PORTNAME)
+LIBSRCS+= getcwd.c putenv.c
+endif
+
+
+.PHONY: beforeinstall-headers install-headers
+
+ifndef NO_BEFOREINSTL
+beforeinstall-headers:
+ @-if [ ! -d $(HEADERDIR) ]; then mkdir $(HEADERDIR); fi
+ @-if [ ! -d $(HEADERDIR)/port ]; then mkdir $(HEADERDIR)/port; fi
+ @-if [ ! -d $(HEADERDIR)/port/$(PORTNAME) ]; \
+ then mkdir $(HEADERDIR)/port/$(PORTNAME); fi
+ @-if [ ! -d $(HEADERDIR)/include ]; \
+ then mkdir $(HEADERDIR)/include; fi
+ @-if [ ! -d $(HEADERDIR)/lib ]; \
+ then mkdir $(HEADERDIR)/lib; fi
+ @-if [ ! -d $(HEADERDIR)/libpq ]; \
+ then mkdir $(HEADERDIR)/libpq; fi
+ @-if [ ! -d $(HEADERDIR)/utils ]; \
+ then mkdir $(HEADERDIR)/utils; fi
+else
+beforeinstall-headers: .dosomething
+endif
+
+HEADERFILES = include/postgres.h \
+ libpq/pqcomm.h \
+ libpq/libpq-fs.h \
+ lib/dllist.h \
+ utils/geo-decls.h
+
+ifeq ($(PORTNAME), hpux)
+HEADERFILES += port/hpux/fixade.h
+endif
+
+
+TEMPDIR=/tmp
+
+install-headers: beforeinstall-headers
+ @for i in ${HEADERFILES}; do \
+ echo "Installing $(HEADERDIR)/$$i."; \
+ $(INSTALL) $(INSTLOPTS) $(srcdir)/backend/$$i $(HEADERDIR)/$$i; \
+ done
+ $(INSTALL) $(INSTLOPTS) libpq-fe.h $(HEADERDIR)/libpq-fe.h
+ @mv -f $(HEADERDIR)/include/* $(HEADERDIR)
+ @rmdir $(HEADERDIR)/include
+# XXX - installing fmgr.h depends on the backend being built
+ $(INSTALL) $(INSTLOPTS) $(srcdir)/backend/$(objdir)/fmgr.h $(HEADERDIR)/fmgr.h
+ @rm -f $(TEMPDIR)/c.h
+ @echo "#undef PORTNAME" > $(TEMPDIR)/c.h
+ @echo "#define PORTNAME $(PORTNAME)" >> $(TEMPDIR)/c.h
+ @echo "#undef PORTNAME_$(PORTNAME)" >> $(TEMPDIR)/c.h
+ @echo "#define PORTNAME_$(PORTNAME)" >> $(TEMPDIR)/c.h
+ @cat $(srcdir)/backend/include/c.h >> $(TEMPDIR)/c.h
+ $(INSTALL) $(INSTLOPTS) $(TEMPDIR)/c.h $(HEADERDIR)/c.h
+ @rm -f $(TEMPDIR)/postgres.h
+# hardwire NAMEDATALEN and OIDNAMELEN into the postgres.h for this installation
+ @echo "#define NAMEDATALEN $(NAMEDATALEN)" >> $(TEMPDIR)/postgres.h
+ @echo "#define OIDNAMELEN $(OIDNAMELEN)" >> $(TEMPDIR)/postgres.h
+ @cat $(srcdir)/backend/include/postgres.h >> $(TEMPDIR)/postgres.h
+ $(INSTALL) $(INSTLOPTS) $(TEMPDIR)/postgres.h $(HEADERDIR)/postgres.h
+
+install:: install-headers
+
+include $(MKDIR)/postgres.lib.mk
+
diff --git a/src/interfaces/libpq/README b/src/interfaces/libpq/README
new file mode 100644
index 00000000000..e581950bf23
--- /dev/null
+++ b/src/interfaces/libpq/README
@@ -0,0 +1 @@
+This directory contains the C version of Libpq, the POSTGRES frontend library.
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
new file mode 100644
index 00000000000..cce02b64fc0
--- /dev/null
+++ b/src/interfaces/libpq/fe-auth.c
@@ -0,0 +1,544 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-auth.c--
+ * The front-end (client) authorization routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * INTERFACE ROUTINES
+ * frontend (client) routines:
+ * fe_sendauth send authentication information
+ * fe_getauthname get user's name according to the client side
+ * of the authentication system
+ * fe_setauthsvc set frontend authentication service
+ * fe_getauthsvc get current frontend authentication service
+ *
+ *
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h> /* for MAX{HOSTNAME,PATH}LEN, NOFILE */
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include "libpq/pqcomm.h"
+
+#include "libpq-fe.h"
+#include "fe-auth.h"
+
+/*----------------------------------------------------------------
+ * common definitions for generic fe/be routines
+ *----------------------------------------------------------------
+ */
+
+struct authsvc {
+ char name[16]; /* service nickname (for command line) */
+ MsgType msgtype; /* startup packet header type */
+ int allowed; /* initially allowed (before command line
+ * option parsing)?
+ */
+};
+
+/*
+ * Command-line parsing routines use this structure to map nicknames
+ * onto service types (and the startup packets to use with them).
+ *
+ * Programs receiving an authentication request use this structure to
+ * decide which authentication service types are currently permitted.
+ * By default, all authentication systems compiled into the system are
+ * allowed. Unauthenticated connections are disallowed unless there
+ * isn't any authentication system.
+ */
+static struct authsvc authsvcs[] = {
+#ifdef KRB4
+ { "krb4", STARTUP_KRB4_MSG, 1 },
+ { "kerberos", STARTUP_KRB4_MSG, 1 },
+#endif /* KRB4 */
+#ifdef KRB5
+ { "krb5", STARTUP_KRB5_MSG, 1 },
+ { "kerberos", STARTUP_KRB5_MSG, 1 },
+#endif /* KRB5 */
+ { UNAUTHNAME, STARTUP_MSG,
+#if defined(KRB4) || defined(KRB5)
+ 0
+#else /* !(KRB4 || KRB5) */
+ 1
+#endif /* !(KRB4 || KRB5) */
+ }
+};
+
+static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
+
+#ifdef KRB4
+/*----------------------------------------------------------------
+ * MIT Kerberos authentication system - protocol version 4
+ *----------------------------------------------------------------
+ */
+
+#include "krb.h"
+
+/* for some reason, this is not defined in krb.h ... */
+extern char *tkt_string(void);
+
+/*
+ * pg_krb4_init -- initialization performed before any Kerberos calls are made
+ *
+ * For v4, all we need to do is make sure the library routines get the right
+ * ticket file if we want them to see a special one. (They will open the file
+ * themselves.)
+ */
+static void pg_krb4_init()
+{
+ char *realm;
+ static init_done = 0;
+
+ if (init_done)
+ return;
+ init_done = 1;
+
+ /*
+ * If the user set PGREALM, then we use a ticket file with a special
+ * name: <usual-ticket-file-name>@<PGREALM-value>
+ */
+ if (realm = getenv("PGREALM")) {
+ char tktbuf[MAXPATHLEN];
+
+ (void) sprintf(tktbuf, "%s@%s", tkt_string(), realm);
+ krb_set_tkt_string(tktbuf);
+ }
+}
+
+/*
+ * pg_krb4_authname -- returns a pointer to static space containing whatever
+ * name the user has authenticated to the system
+ *
+ * We obtain this information by digging around in the ticket file.
+ */
+static char *
+pg_krb4_authname(char* PQerrormsg)
+{
+ char instance[INST_SZ];
+ char realm[REALM_SZ];
+ int status;
+ static char name[SNAME_SZ+1] = "";
+
+ if (name[0])
+ return(name);
+
+ pg_krb4_init();
+
+ name[SNAME_SZ] = '\0';
+ status = krb_get_tf_fullname(tkt_string(), name, instance, realm);
+ if (status != KSUCCESS) {
+ (void) sprintf(PQerrormsg,
+ "pg_krb4_authname: krb_get_tf_fullname: %s\n",
+ krb_err_txt[status]);
+ return((char *) NULL);
+ }
+ return(name);
+}
+
+/*
+ * pg_krb4_sendauth -- client routine to send authentication information to
+ * the server
+ *
+ * This routine does not do mutual authentication, nor does it return enough
+ * information to do encrypted connections. But then, if we want to do
+ * encrypted connections, we'll have to redesign the whole RPC mechanism
+ * anyway.
+ *
+ * If the user is too lazy to feed us a hostname, we try to come up with
+ * something other than "localhost" since the hostname is used as an
+ * instance and instance names in v4 databases are usually actual hostnames
+ * (canonicalized to omit all domain suffixes).
+ */
+static int
+pg_krb4_sendauth(char* PQerrormsg, int sock,
+ struct sockaddr_in *laddr,
+ struct sockaddr_in *raddr,
+ char *hostname)
+{
+ long krbopts = 0; /* one-way authentication */
+ KTEXT_ST clttkt;
+ int status;
+ char hostbuf[MAXHOSTNAMELEN];
+ char *realm = getenv("PGREALM"); /* NULL == current realm */
+
+ if (!hostname || !(*hostname)) {
+ if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
+ strcpy(hostbuf, "localhost");
+ hostname = hostbuf;
+ }
+
+ pg_krb4_init();
+
+ status = krb_sendauth(krbopts,
+ sock,
+ &clttkt,
+ PG_KRB_SRVNAM,
+ hostname,
+ realm,
+ (u_long) 0,
+ (MSG_DAT *) NULL,
+ (CREDENTIALS *) NULL,
+ (Key_schedule *) NULL,
+ laddr,
+ raddr,
+ PG_KRB4_VERSION);
+ if (status != KSUCCESS) {
+ (void) sprintf(PQerrormsg,
+ "pg_krb4_sendauth: kerberos error: %s\n",
+ krb_err_txt[status]);
+ return(STATUS_ERROR);
+ }
+ return(STATUS_OK);
+}
+
+#endif /* KRB4 */
+
+#ifdef KRB5
+/*----------------------------------------------------------------
+ * MIT Kerberos authentication system - protocol version 5
+ *----------------------------------------------------------------
+ */
+
+#include "krb5/krb5.h"
+
+/*
+ * pg_an_to_ln -- return the local name corresponding to an authentication
+ * name
+ *
+ * XXX Assumes that the first aname component is the user name. This is NOT
+ * necessarily so, since an aname can actually be something out of your
+ * worst X.400 nightmare, like
+ * ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
+ * Note that the MIT an_to_ln code does the same thing if you don't
+ * provide an aname mapping database...it may be a better idea to use
+ * krb5_an_to_ln, except that it punts if multiple components are found,
+ * and we can't afford to punt.
+ */
+static char *
+pg_an_to_ln(char *aname)
+{
+ char *p;
+
+ if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
+ *p = '\0';
+ return(aname);
+}
+
+
+/*
+ * pg_krb5_init -- initialization performed before any Kerberos calls are made
+ *
+ * With v5, we can no longer set the ticket (credential cache) file name;
+ * we now have to provide a file handle for the open (well, "resolved")
+ * ticket file everywhere.
+ *
+ */
+static int
+krb5_ccache pg_krb5_init()
+{
+ krb5_error_code code;
+ char *realm, *defname;
+ char tktbuf[MAXPATHLEN];
+ static krb5_ccache ccache = (krb5_ccache) NULL;
+
+ if (ccache)
+ return(ccache);
+
+ /*
+ * If the user set PGREALM, then we use a ticket file with a special
+ * name: <usual-ticket-file-name>@<PGREALM-value>
+ */
+ if (!(defname = krb5_cc_default_name())) {
+ (void) sprintf(PQerrormsg,
+ "pg_krb5_init: krb5_cc_default_name failed\n");
+ return((krb5_ccache) NULL);
+ }
+ (void) strcpy(tktbuf, defname);
+ if (realm = getenv("PGREALM")) {
+ (void) strcat(tktbuf, "@");
+ (void) strcat(tktbuf, realm);
+ }
+
+ if (code = krb5_cc_resolve(tktbuf, &ccache)) {
+ (void) sprintf(PQerrormsg,
+ "pg_krb5_init: Kerberos error %d in krb5_cc_resolve\n",
+ code);
+ com_err("pg_krb5_init", code, "in krb5_cc_resolve");
+ return((krb5_ccache) NULL);
+ }
+ return(ccache);
+}
+
+/*
+ * pg_krb5_authname -- returns a pointer to static space containing whatever
+ * name the user has authenticated to the system
+ *
+ * We obtain this information by digging around in the ticket file.
+ */
+static char *
+pg_krb5_authname(char* PQerrormsg)
+{
+ krb5_ccache ccache;
+ krb5_principal principal;
+ krb5_error_code code;
+ static char *authname = (char *) NULL;
+
+ if (authname)
+ return(authname);
+
+ ccache = pg_krb5_init(); /* don't free this */
+
+ if (code = krb5_cc_get_principal(ccache, &principal)) {
+ (void) sprintf(PQerrormsg,
+ "pg_krb5_authname: Kerberos error %d in krb5_cc_get_principal\n",
+ code);
+ com_err("pg_krb5_authname", code, "in krb5_cc_get_principal");
+ return((char *) NULL);
+ }
+ if (code = krb5_unparse_name(principal, &authname)) {
+ (void) sprintf(PQerrormsg,
+ "pg_krb5_authname: Kerberos error %d in krb5_unparse_name\n",
+ code);
+ com_err("pg_krb5_authname", code, "in krb5_unparse_name");
+ krb5_free_principal(principal);
+ return((char *) NULL);
+ }
+ krb5_free_principal(principal);
+ return(pg_an_to_ln(authname));
+}
+
+/*
+ * pg_krb5_sendauth -- client routine to send authentication information to
+ * the server
+ *
+ * This routine does not do mutual authentication, nor does it return enough
+ * information to do encrypted connections. But then, if we want to do
+ * encrypted connections, we'll have to redesign the whole RPC mechanism
+ * anyway.
+ *
+ * Server hostnames are canonicalized v4-style, i.e., all domain suffixes
+ * are simply chopped off. Hence, we are assuming that you've entered your
+ * server instances as
+ * <value-of-PG_KRB_SRVNAM>/<canonicalized-hostname>
+ * in the PGREALM (or local) database. This is probably a bad assumption.
+ */
+static int
+pg_krb5_sendauth(char* PQerrormsg,int sock,
+ struct sockaddr_in *laddr,
+ struct sockaddr_in *raddr,
+ char *hostname)
+{
+ char servbuf[MAXHOSTNAMELEN + 1 +
+ sizeof(PG_KRB_SRVNAM)];
+ char *hostp;
+ char *realm;
+ krb5_error_code code;
+ krb5_principal client, server;
+ krb5_ccache ccache;
+ krb5_error *error = (krb5_error *) NULL;
+
+ ccache = pg_krb5_init(); /* don't free this */
+
+ /*
+ * set up client -- this is easy, we can get it out of the ticket
+ * file.
+ */
+ if (code = krb5_cc_get_principal(ccache, &client)) {
+ (void) sprintf(PQerrormsg,
+ "pg_krb5_sendauth: Kerberos error %d in krb5_cc_get_principal\n",
+ code);
+ com_err("pg_krb5_sendauth", code, "in krb5_cc_get_principal");
+ return(STATUS_ERROR);
+ }
+
+ /*
+ * set up server -- canonicalize as described above
+ */
+ (void) strcpy(servbuf, PG_KRB_SRVNAM);
+ *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
+ if (hostname || *hostname) {
+ (void) strncpy(++hostp, hostname, MAXHOSTNAMELEN);
+ } else {
+ if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
+ (void) strcpy(hostp, "localhost");
+ }
+ if (hostp = strchr(hostp, '.'))
+ *hostp = '\0';
+ if (realm = getenv("PGREALM")) {
+ (void) strcat(servbuf, "@");
+ (void) strcat(servbuf, realm);
+ }
+ if (code = krb5_parse_name(servbuf, &server)) {
+ (void) sprintf(PQerrormsg,
+ "pg_krb5_sendauth: Kerberos error %d in krb5_parse_name\n",
+ code);
+ com_err("pg_krb5_sendauth", code, "in krb5_parse_name");
+ krb5_free_principal(client);
+ return(STATUS_ERROR);
+ }
+
+ /*
+ * The only thing we want back from krb5_sendauth is an error status
+ * and any error messages.
+ */
+ if (code = krb5_sendauth((krb5_pointer) &sock,
+ PG_KRB5_VERSION,
+ client,
+ server,
+ (krb5_flags) 0,
+ (krb5_checksum *) NULL,
+ (krb5_creds *) NULL,
+ ccache,
+ (krb5_int32 *) NULL,
+ (krb5_keyblock **) NULL,
+ &error,
+ (krb5_ap_rep_enc_part **) NULL)) {
+ if ((code == KRB5_SENDAUTH_REJECTED) && error) {
+ (void) sprintf(PQerrormsg,
+ "pg_krb5_sendauth: authentication rejected: \"%*s\"\n",
+ error->text.length, error->text.data);
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ } else {
+ (void) sprintf(PQerrormsg,
+ "pg_krb5_sendauth: Kerberos error %d in krb5_sendauth\n",
+ code);
+ com_err("pg_krb5_sendauth", code, "in krb5_sendauth");
+ }
+ }
+ krb5_free_principal(client);
+ krb5_free_principal(server);
+ return(code ? STATUS_ERROR : STATUS_OK);
+}
+
+#endif /* KRB5 */
+
+
+/*
+ * fe_sendauth -- client demux routine for outgoing authentication information
+ */
+int
+fe_sendauth(MsgType msgtype, Port *port, char *hostname, char* PQerrormsg)
+{
+ switch (msgtype) {
+#ifdef KRB4
+ case STARTUP_KRB4_MSG:
+ if (pg_krb4_sendauth(PQerrormsg, port->sock, &port->laddr,
+ &port->raddr,
+ hostname) != STATUS_OK) {
+ (void) sprintf(PQerrormsg,
+ "fe_sendauth: krb4 authentication failed\n");
+/* fputs(PQerrormsg, stderr); */
+ return(STATUS_ERROR);
+ }
+ break;
+#endif
+#ifdef KRB5
+ case STARTUP_KRB5_MSG:
+ if (pg_krb5_sendauth(PQerrormsg,port->sock, &port->laddr,
+ &port->raddr,
+ hostname) != STATUS_OK) {
+ (void) sprintf(PQerrormsg,
+ "fe_sendauth: krb5 authentication failed\n");
+ return(STATUS_ERROR);
+ }
+ break;
+#endif
+ case STARTUP_MSG:
+ break;
+ default:
+ break;
+ }
+ return(STATUS_OK);
+}
+
+/*
+ * fe_setauthsvc
+ * fe_getauthsvc
+ *
+ * Set/return the authentication service currently selected for use by the
+ * frontend. (You can only use one in the frontend, obviously.)
+ */
+static pg_authsvc = -1;
+
+void
+fe_setauthsvc(char *name, char* PQerrormsg)
+{
+ int i;
+
+ for (i = 0; i < n_authsvcs; ++i)
+ if (!strcmp(name, authsvcs[i].name)) {
+ pg_authsvc = i;
+ break;
+ }
+ if (i == n_authsvcs) {
+ (void) sprintf(PQerrormsg,
+ "fe_setauthsvc: invalid name: %s, ignoring...\n",
+ name);
+ }
+ return;
+}
+
+MsgType
+fe_getauthsvc(char* PQerrormsg)
+{
+ if (pg_authsvc < 0 || pg_authsvc >= n_authsvcs)
+ fe_setauthsvc(DEFAULT_CLIENT_AUTHSVC,PQerrormsg);
+ return(authsvcs[pg_authsvc].msgtype);
+}
+
+/*
+ * fe_getauthname -- returns a pointer to static space containing whatever
+ * name the user has authenticated to the system
+ * if there is an error, return the error message in PQerrormsg
+ */
+char*
+fe_getauthname(char* PQerrormsg)
+{
+ char *name = (char *) NULL;
+ MsgType authsvc;
+
+ authsvc = fe_getauthsvc(PQerrormsg);
+ switch ((int) authsvc) {
+#ifdef KRB4
+ case STARTUP_KRB4_MSG:
+ name = pg_krb4_authname(PQerrormsg);
+ break;
+#endif
+#ifdef KRB5
+ case STARTUP_KRB5_MSG:
+ name = pg_krb5_authname(PQerrormsg);
+ break;
+#endif
+ case STARTUP_MSG:
+ {
+ struct passwd *pw = getpwuid(getuid());
+ if (pw &&
+ pw->pw_name &&
+ (name = (char *) malloc(strlen(pw->pw_name) + 1))) {
+ (void) strcpy(name, pw->pw_name);
+ }
+ }
+ break;
+ default:
+ (void) sprintf(PQerrormsg,
+ "fe_getauthname: invalid authentication system: %d\n",
+ authsvc);
+ break;
+ }
+ return(name);
+}
+
+
diff --git a/src/interfaces/libpq/fe-auth.h b/src/interfaces/libpq/fe-auth.h
new file mode 100644
index 00000000000..d5b8ecd8f76
--- /dev/null
+++ b/src/interfaces/libpq/fe-auth.h
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-auth.h
+ *
+ * Definitions for network authentication routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: fe-auth.h,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FE_AUTH_H
+#define FE_AUTH_H
+
+/*----------------------------------------------------------------
+ * Common routines and definitions
+ *----------------------------------------------------------------
+ */
+
+/* what we call "no authentication system" */
+#define UNAUTHNAME "unauth"
+
+/* what a frontend uses by default */
+#if !defined(KRB4) && !defined(KRB5)
+#define DEFAULT_CLIENT_AUTHSVC UNAUTHNAME
+#else /* KRB4 || KRB5 */
+#define DEFAULT_CLIENT_AUTHSVC "kerberos"
+#endif /* KRB4 || KRB5 */
+
+extern int fe_sendauth(MsgType msgtype, Port *port, char *hostname, char* PQerromsg);
+extern void fe_setauthsvc(char *name, char* PQerrormsg);
+
+#define PG_KRB4_VERSION "PGVER4.1" /* at most KRB_SENDAUTH_VLEN chars */
+#define PG_KRB5_VERSION "PGVER5.1"
+
+#endif /* FE_AUTH_H */
+
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
new file mode 100644
index 00000000000..97a909e1055
--- /dev/null
+++ b/src/interfaces/libpq/fe-connect.c
@@ -0,0 +1,460 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-connect.c--
+ * functions related to setting up a connection to the backend
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include "libpq/pqcomm.h" /* for decls of MsgType, PacketBuf, StartupInfo */
+#include "fe-auth.h"
+#include "libpq-fe.h"
+
+#if defined(PORTNAME_ultrix4) || defined(PORTNAME_next)
+ /* ultrix is lame and doesn't have strdup in libc for some reason */
+ /* [TRH] So doesn't NEXTSTEP. But whaddaya expect for a non-ANSI
+standard function? (My, my. Touchy today, are we?) */
+static
+char *
+strdup(char *string)
+{
+ char *nstr;
+
+ nstr = strcpy((char *)malloc(strlen(string)+1), string);
+ return nstr;
+}
+#endif
+
+/* use a local version instead of the one found in pqpacket.c */
+static ConnStatusType connectDB(PGconn *conn);
+
+static int packetSend(Port *port, PacketBuf *buf, PacketLen len,
+ bool nonBlocking);
+static void startup2PacketBuf(StartupInfo* s, PacketBuf* res);
+static void freePGconn(PGconn *conn);
+static void closePGconn(PGconn *conn);
+
+#define NOTIFYLIST_INITIAL_SIZE 10
+#define NOTIFYLIST_GROWBY 10
+
+/* ----------------
+ * PQsetdb
+ *
+ * establishes a connectin to a postgres backend through the postmaster
+ * at the specified host and port.
+ *
+ * returns a PGconn* which is needed for all subsequent libpq calls
+ * if the status field of the connection returned is CONNECTION_BAD,
+ * then some fields may be null'ed out instead of having valid values
+ * ----------------
+ */
+PGconn*
+PQsetdb(char *pghost, char* pgport, char* pgoptions, char* pgtty, char* dbName)
+{
+ PGconn *conn;
+ char *tmp;
+
+ conn = (PGconn*)malloc(sizeof(PGconn));
+
+ conn->Pfout = NULL;
+ conn->Pfin = NULL;
+ conn->Pfdebug = NULL;
+ conn->port = NULL;
+ conn->notifyList = DLNewList();
+
+ if (!pghost || pghost[0] == '\0') {
+ if (!(tmp = getenv("PGHOST"))) {
+ tmp = DefaultHost;
+ }
+ conn->pghost = strdup(tmp);
+ } else
+ conn->pghost = strdup(pghost);
+
+ if (!pgport || pgport == '\0') {
+ if (!(tmp = getenv("PGPORT"))) {
+ tmp = POSTPORT;
+ }
+ conn->pgport = strdup(tmp);
+ } else
+ conn->pgport = strdup(pgport);
+
+ if (!pgtty || pgtty == '\0') {
+ if (!(tmp = getenv("PGTTY"))) {
+ tmp = DefaultTty;
+ }
+ conn->pgtty = strdup(tmp);
+ } else
+ conn->pgtty = strdup(pgtty);
+
+ if (!pgoptions || pgoptions == '\0') {
+ if (!(tmp = getenv("PGOPTIONS"))) {
+ tmp = DefaultOption;
+ }
+ conn->pgoptions = strdup(tmp);
+ } else
+ conn->pgoptions = strdup(pgoptions);
+
+ if (!dbName || dbName[0] == '\0') {
+ char errorMessage[ERROR_MSG_LENGTH];
+ if (!(tmp = getenv("PGDATABASE")) &&
+ !(tmp = fe_getauthname(errorMessage))) {
+ sprintf(conn->errorMessage,
+ "FATAL: PQsetdb: Unable to determine a database name!\n");
+/* pqdebug("%s", conn->errorMessage); */
+ conn->dbName = NULL;
+ return conn;
+ }
+ conn->dbName = strdup(tmp);
+ } else
+ conn->dbName = strdup(dbName);
+
+ conn->status = connectDB(conn);
+ return conn;
+}
+
+/*
+ * connectDB -
+ * make a connection to the database, returns 1 if successful or 0 if not
+ *
+ */
+static ConnStatusType
+connectDB(PGconn *conn)
+{
+ struct hostent *hp;
+
+ StartupInfo startup;
+ PacketBuf pacBuf;
+ int status;
+ MsgType msgtype;
+ int laddrlen = sizeof(struct sockaddr);
+ Port *port = conn->port;
+ int portno;
+ PGresult *res;
+
+ char *user;
+ /*
+ //
+ // Initialize the startup packet.
+ //
+ // This data structure is used for the seq-packet protocol. It
+ // describes the frontend-backend connection.
+ //
+ //
+ */
+ user = fe_getauthname(conn->errorMessage);
+ if (!user)
+ goto connect_errReturn;
+ strncpy(startup.database,conn->dbName,sizeof(startup.database));
+ strncpy(startup.user,user,sizeof(startup.user));
+ strncpy(startup.tty,conn->pgtty,sizeof(startup.tty));
+ if (conn->pgoptions) {
+ strncpy(startup.options,conn->pgoptions, sizeof(startup.options));
+ }
+ else
+ startup.options[0]='\0';
+ startup.execFile[0]='\0'; /* not used */
+
+ /*
+ //
+ // Open a connection to postmaster/backend.
+ //
+ */
+ port = (Port *) malloc(sizeof(Port));
+ memset((char *) port, 0, sizeof(Port));
+
+ if (!(hp = gethostbyname(conn->pghost)) || hp->h_addrtype != AF_INET) {
+ (void) sprintf(conn->errorMessage,
+ "connectDB() -- unknown hostname: %s\n",
+ conn->pghost);
+ goto connect_errReturn;
+ }
+ memset((char *) &port->raddr, 0, sizeof(port->raddr));
+ memmove((char *) &(port->raddr.sin_addr),
+ (char *) hp->h_addr,
+ hp->h_length);
+ port->raddr.sin_family = AF_INET;
+ portno = atoi(conn->pgport);
+ port->raddr.sin_port = htons((unsigned short)(portno));
+
+ /* connect to the server */
+ if ((port->sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ (void) sprintf(conn->errorMessage,
+ "connectDB() -- socket() failed: errno=%d\n%s\n",
+ errno, strerror(errno));
+ goto connect_errReturn;
+ }
+ if (connect(port->sock, (struct sockaddr *)&port->raddr,
+ sizeof(port->raddr)) < 0) {
+ (void) sprintf(conn->errorMessage,
+ "connectDB() failed: Is the postmaster running at '%s' on port '%s'?\n",
+ conn->pghost,conn->pgport);
+ goto connect_errReturn;
+ }
+
+
+ /* fill in the client address */
+ if (getsockname(port->sock, (struct sockaddr *) &port->laddr,
+ &laddrlen) < 0) {
+ (void) sprintf(conn->errorMessage,
+ "connectDB() -- getsockname() failed: errno=%d\n%s\n",
+ errno, strerror(errno));
+ goto connect_errReturn;
+ }
+
+ /* by this point, connection has been opened */
+ msgtype = fe_getauthsvc(conn->errorMessage);
+
+/* pacBuf = startup2PacketBuf(&startup);*/
+ startup2PacketBuf(&startup, &pacBuf);
+ pacBuf.msgtype = htonl(msgtype);
+ status = packetSend(port, &pacBuf, sizeof(PacketBuf), BLOCKING);
+
+ if (status == STATUS_ERROR)
+ {
+ sprintf(conn->errorMessage,
+ "connectDB() -- couldn't send complete packet: errno=%d\n%s\n", errno,strerror(errno));
+ goto connect_errReturn;
+ }
+
+ /* authenticate as required*/
+ if (fe_sendauth(msgtype, port, conn->pghost,
+ conn->errorMessage) != STATUS_OK) {
+ (void) sprintf(conn->errorMessage,
+ "connectDB() -- authentication failed with %s\n",
+ conn->pghost);
+ goto connect_errReturn;
+ }
+
+ /* set up the socket file descriptors */
+ conn->Pfout = fdopen(port->sock, "w");
+ conn->Pfin = fdopen(dup(port->sock), "r");
+ if (!conn->Pfout || !conn->Pfin) {
+ (void) sprintf(conn->errorMessage,
+ "connectDB() -- fdopen() failed: errno=%d\n%s\n",
+ errno, strerror(errno));
+ goto connect_errReturn;
+ }
+
+ conn->port = port;
+
+ /* we have a connection now,
+ send a blank query down to make sure the database exists*/
+ res = PQexec(conn," ");
+ if (res == NULL || res->resultStatus != PGRES_EMPTY_QUERY) {
+ /* error will already be in conn->errorMessage */
+ goto connect_errReturn;
+ }
+ free(res);
+ return CONNECTION_OK;
+
+connect_errReturn:
+ return CONNECTION_BAD;
+
+}
+
+/*
+ * freePGconn
+ * - free the PGconn data structure
+ *
+ */
+static void
+freePGconn(PGconn *conn)
+{
+ if (conn->pghost) free(conn->pghost);
+ if (conn->pgtty) free(conn->pgtty);
+ if (conn->pgoptions) free(conn->pgoptions);
+ if (conn->pgport) free(conn->pgport);
+ if (conn->dbName) free(conn->dbName);
+ if (conn->notifyList) DLFreeList(conn->notifyList);
+ free(conn);
+}
+
+/*
+ closePGconn
+ - properly close a connection to the backend
+*/
+static void
+closePGconn(PGconn *conn)
+{
+ fputs("X\0", conn->Pfout);
+ fflush(conn->Pfout);
+ if (conn->Pfout) fclose(conn->Pfout);
+ if (conn->Pfin) fclose(conn->Pfin);
+ if (conn->Pfdebug) fclose(conn->Pfdebug);
+}
+
+/*
+ PQfinish:
+ properly close a connection to the backend
+ also frees the PGconn data structure so it shouldn't be re-used
+ after this
+*/
+void
+PQfinish(PGconn *conn)
+{
+ if (conn->status == CONNECTION_OK)
+ closePGconn(conn);
+ freePGconn(conn);
+}
+
+/* PQreset :
+ resets the connection to the backend
+ closes the existing connection and makes a new one
+*/
+void
+PQreset(PGconn *conn)
+{
+ closePGconn(conn);
+ conn->status = connectDB(conn);
+}
+
+/*
+ * PacketSend()
+ *
+ this is just like PacketSend(), defined in backend/libpq/pqpacket.c
+ but we define it here to avoid linking in all of libpq.a
+
+ * packetSend -- send a single-packet message.
+ *
+ * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
+ * SIDE_EFFECTS: may block.
+ * NOTES: Non-blocking writes would significantly complicate
+ * buffer management. For now, we're not going to do it.
+ *
+*/
+static int
+packetSend(Port *port,
+ PacketBuf *buf,
+ PacketLen len,
+ bool nonBlocking)
+{
+ PacketLen totalLen;
+ int addrLen = sizeof(struct sockaddr_in);
+
+ totalLen = len;
+
+ len = sendto(port->sock, (Addr) buf, totalLen, /* flags */ 0,
+ (struct sockaddr *)&(port->raddr), addrLen);
+
+ if (len < totalLen) {
+ return(STATUS_ERROR);
+ }
+
+ return(STATUS_OK);
+}
+
+/*
+ * startup2PacketBuf()
+ *
+ * this is just like StartupInfo2Packet(), defined in backend/libpq/pqpacket.c
+ * but we repeat it here so we don't have to link in libpq.a
+ *
+ * converts a StartupInfo structure to a PacketBuf
+ */
+static void
+startup2PacketBuf(StartupInfo* s, PacketBuf* res)
+{
+ char* tmp;
+
+/* res = (PacketBuf*)malloc(sizeof(PacketBuf)); */
+ res->len = htonl(sizeof(PacketBuf));
+ /* use \n to delimit the strings */
+ res->data[0] = '\0';
+
+ tmp= res->data;
+
+ strncpy(tmp, s->database, sizeof(s->database));
+ tmp += sizeof(s->database);
+ strncpy(tmp, s->user, sizeof(s->user));
+ tmp += sizeof(s->user);
+ strncpy(tmp, s->options, sizeof(s->options));
+ tmp += sizeof(s->options);
+ strncpy(tmp, s->execFile, sizeof(s->execFile));
+ tmp += sizeof(s->execFile);
+ strncpy(tmp, s->tty, sizeof(s->execFile));
+
+}
+
+
+/* =========== accessor functions for PGconn ========= */
+char*
+PQdb(PGconn* conn)
+{
+ return conn->dbName;
+}
+
+char*
+PQhost(PGconn* conn)
+{
+ return conn->pghost;
+}
+
+char*
+PQoptions(PGconn* conn)
+{
+ return conn->pgoptions;
+}
+
+char*
+PQtty(PGconn* conn)
+{
+ return conn->pgtty;
+}
+
+char*
+PQport(PGconn* conn)
+{
+ return conn->pgport;
+}
+
+ConnStatusType
+PQstatus(PGconn* conn)
+{
+ return conn->status;
+}
+
+char*
+PQerrorMessage(PGconn* conn)
+{
+ return conn->errorMessage;
+}
+
+void
+PQtrace(PGconn *conn, FILE* debug_port)
+{
+ if (conn == NULL ||
+ conn->status == CONNECTION_BAD) {
+ return;
+ }
+ PQuntrace(conn);
+ conn->Pfdebug = debug_port;
+}
+
+void
+PQuntrace(PGconn *conn)
+{
+ if (conn == NULL ||
+ conn->status == CONNECTION_BAD) {
+ return;
+ }
+ if (conn->Pfdebug) {
+ fflush(conn->Pfdebug);
+ fclose(conn->Pfdebug);
+ conn->Pfdebug = NULL;
+ }
+}
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
new file mode 100644
index 00000000000..78854ed73fe
--- /dev/null
+++ b/src/interfaces/libpq/fe-exec.c
@@ -0,0 +1,1061 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-exec.c--
+ * functions related to sending a query down to the backend
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "postgres.h"
+#include "libpq/pqcomm.h"
+#include "libpq-fe.h"
+#include <signal.h>
+
+/* the tuples array in a PGresGroup has to grow to accommodate the tuples */
+/* returned. Each time, we grow by this much: */
+#define TUPARR_GROW_BY 100
+
+/* keep this in same order as ExecStatusType in pgtclCmds.h */
+char* pgresStatus[] = {
+ "PGRES_EMPTY_QUERY",
+ "PGRES_COMMAND_OK",
+ "PGRES_TUPLES_OK",
+ "PGRES_BAD_RESPONSE",
+ "PGRES_NONFATAL_ERROR",
+ "PGRES_FATAL_ERROR"
+};
+
+
+static PGresult* makePGresult(PGconn *conn, char *pname);
+static void addTuple(PGresult *res, PGresAttValue *tup);
+static PGresAttValue* getTuple(PGconn *conn, PGresult *res, int binary);
+static PGresult* makeEmptyPGresult(PGconn *conn, ExecStatusType status);
+static void fill(int length, int max, char filler, FILE *fp);
+
+/*
+ * PQclear -
+ * free's the memory associated with a PGresult
+ *
+ */
+void
+PQclear(PGresult* res)
+{
+ int i,j;
+
+ if (!res)
+ return;
+
+ /* free all the tuples */
+ for (i=0;i<res->ntups;i++) {
+ for (j=0;j<res->numAttributes;j++) {
+ if (res->tuples[i][j].value)
+ free(res->tuples[i][j].value);
+ }
+ free(res->tuples[i]);
+ }
+ free(res->tuples);
+
+ /* free all the attributes */
+ for (i=0;i<res->numAttributes;i++) {
+ if (res->attDescs[i].name)
+ free(res->attDescs[i].name);
+ }
+ free(res->attDescs);
+
+ /* free the structure itself */
+ free(res);
+}
+
+/*
+ * PGresult -
+ * returns a newly allocated, initialized PGresult
+ *
+ */
+
+static PGresult*
+makeEmptyPGresult(PGconn *conn, ExecStatusType status)
+{
+ PGresult *result;
+
+ result = (PGresult*)malloc(sizeof(PGresult));
+
+ result->conn = conn;
+ result->ntups = 0;
+ result->numAttributes = 0;
+ result->attDescs = NULL;
+ result->tuples = NULL;
+ result->tupArrSize = 0;
+ result->resultStatus = status;
+ result->cmdStatus[0] = '\0';
+ result->binary = 0;
+ return result;
+}
+
+/*
+ * getTuple -
+ * get the next tuple from the stream
+ *
+ * the CALLER is responsible from freeing the PGresAttValue returned
+ */
+
+static PGresAttValue*
+getTuple(PGconn *conn, PGresult* result, int binary)
+{
+ char bitmap[MAX_FIELDS]; /* the backend sends us a bitmap of */
+ /* which attributes are null */
+ int bitmap_index = 0;
+ int i;
+ int nbytes; /* the number of bytes in bitmap */
+ char bmap; /* One byte of the bitmap */
+ int bitcnt = 0; /* number of bits examined in current byte */
+ int vlen; /* length of the current field value */
+ FILE *Pfin = conn->Pfin;
+ FILE *Pfdebug = conn->Pfdebug;
+
+ PGresAttValue* tup;
+
+ int nfields = result->numAttributes;
+
+ result->binary = binary;
+
+ tup = (PGresAttValue*) malloc(nfields * sizeof(PGresAttValue));
+
+ nbytes = nfields / BYTELEN;
+ if ( (nfields % BYTELEN) > 0)
+ nbytes++;
+
+ if (pqGetnchar(bitmap, nbytes, Pfin, Pfdebug) == 1){
+ sprintf(conn->errorMessage,
+ "Error reading null-values bitmap from tuple data stream\n");
+ return NULL;
+ }
+
+ bmap = bitmap[bitmap_index];
+
+ for (i=0;i<nfields;i++) {
+ if (!(bmap & 0200)) {
+ /* if the field value is absent, make it '\0' */
+ /* XXX this makes it impossible to distinguish NULL
+ attributes from "". Is that OK? */
+ tup[i].value = (char*)malloc(1);
+ tup[i].value[0] = '\0';
+ tup[i].len = 0;
+ }
+ else {
+ /* get the value length (the first four bytes are for length) */
+ pqGetInt(&vlen, VARHDRSZ, Pfin, Pfdebug);
+ if (binary == 0) {
+ vlen = vlen - VARHDRSZ;
+ }
+ if (vlen < 0)
+ vlen = 0;
+ tup[i].len = vlen;
+ tup[i].value = (char*) malloc(vlen + 1);
+ /* read in the value; */
+ if (vlen > 0)
+ pqGetnchar((char*)(tup[i].value), vlen, Pfin, Pfdebug);
+ tup[i].value[vlen] = '\0';
+ }
+ /* get the appropriate bitmap */
+ bitcnt++;
+ if (bitcnt == BYTELEN) {
+ bitmap_index++;
+ bmap = bitmap[bitmap_index];
+ bitcnt = 0;
+ } else
+ bmap <<= 1;
+ }
+
+ return tup;
+}
+
+
+/*
+ * addTuple
+ * add a tuple to the PGresult structure, growing it if necessary
+ * to accommodate
+ *
+ */
+static void
+addTuple(PGresult* res, PGresAttValue* tup)
+{
+ if (res->ntups == res->tupArrSize) {
+ /* grow the array */
+ res->tupArrSize += TUPARR_GROW_BY;
+
+ if (res->ntups == 0)
+ res->tuples = (PGresAttValue**)
+ malloc(res->tupArrSize * sizeof(PGresAttValue*));
+ else
+ /* we can use realloc because shallow copying of the structure is okay */
+ res->tuples = (PGresAttValue**)
+ realloc(res->tuples, res->tupArrSize * sizeof(PGresAttValue*));
+ }
+
+ res->tuples[res->ntups] = tup;
+ res->ntups++;
+}
+
+/*
+ * PGresult
+ * fill out the PGresult structure with result tuples from the backend
+ * this is called after query has been successfully run and we have
+ * a portal name
+ *
+ * ASSUMPTION: we assume only *1* tuple group is returned from the backend
+ *
+ * the CALLER is reponsible for free'ing the new PGresult allocated here
+ *
+ */
+
+static PGresult*
+makePGresult(PGconn* conn, char* pname)
+{
+ PGresult* result;
+ int id;
+ int nfields;
+ int i;
+ int done = 0;
+
+ PGresAttValue* newTup;
+
+ FILE* Pfin = conn->Pfin;
+ FILE* Pfdebug = conn->Pfdebug;
+
+ result = makeEmptyPGresult(conn, PGRES_TUPLES_OK);
+
+ /* makePGresult() should only be called when the */
+ /* id of the stream is 'T' to start with */
+
+ /* the next two bytes are the number of fields */
+ if (pqGetInt(&nfields, 2, Pfin, Pfdebug) == 1) {
+ sprintf(conn->errorMessage,
+ "could not get the number of fields from the 'T' message\n");
+ goto makePGresult_badResponse_return;
+ }
+ else
+ result->numAttributes = nfields;
+
+ /* allocate space for the attribute descriptors */
+ if (nfields > 0) {
+ result->attDescs = (PGresAttDesc*) malloc(nfields * sizeof(PGresAttDesc));
+ }
+
+ /* get type info */
+ for (i=0;i<nfields;i++) {
+ char typName[MAX_MESSAGE_LEN];
+ int adtid;
+ int adtsize;
+
+ if ( pqGets(typName, MAX_MESSAGE_LEN, Pfin, Pfdebug) ||
+ pqGetInt(&adtid, 4, Pfin, Pfdebug) ||
+ pqGetInt(&adtsize, 2, Pfin, Pfdebug)) {
+ sprintf(conn->errorMessage,
+ "error reading type information from the 'T' message\n");
+ goto makePGresult_badResponse_return;
+ }
+ result->attDescs[i].name = malloc(strlen(typName)+1);
+ strcpy(result->attDescs[i].name,typName);
+ result->attDescs[i].adtid = adtid;
+ result->attDescs[i].adtsize = adtsize; /* casting from int to int2 here */
+ }
+
+ id = pqGetc(Pfin,Pfdebug);
+
+ /* process the data stream until we're finished */
+ while(!done) {
+ switch (id) {
+ case 'T': /* a new tuple group */
+ sprintf(conn->errorMessage,
+ "makePGresult() -- is not equipped to handle multiple tuple groups.\n");
+ goto makePGresult_badResponse_return;
+ case 'B': /* a tuple in binary format */
+ case 'D': /* a tuple in ASCII format */
+ newTup = getTuple(conn, result, (id == 'B'));
+ if (newTup == NULL)
+ goto makePGresult_badResponse_return;
+ addTuple(result,newTup);
+ break;
+/* case 'A':
+ sprintf(conn->errorMessage, "Asynchronous portals not supported");
+ result->resultStatus = PGRES_NONFATAL_ERROR;
+ return result;
+ break;
+*/
+ case 'C': /* end of portal tuple stream */
+ {
+ char command[MAX_MESSAGE_LEN];
+ pqGets(command,MAX_MESSAGE_LEN, Pfin, Pfdebug); /* read the command tag */
+ done = 1;
+ }
+ break;
+ case 'E': /* errors */
+ if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, Pfin, Pfdebug) == 1) {
+ sprintf(conn->errorMessage,
+ "Error return detected from backend, but error message cannot be read");
+ }
+ result->resultStatus = PGRES_FATAL_ERROR;
+ return result;
+ break;
+ case 'N': /* notices from the backend */
+ if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, Pfin, Pfdebug) == 1) {
+ sprintf(conn->errorMessage,
+ "Notice return detected from backend, but error message cannot be read");
+ } else
+ /* XXXX send Notices to stderr for now */
+ fprintf(stderr, "%s\n", conn->errorMessage);
+ break;
+ default: /* uh-oh
+ this should never happen but frequently does when the
+ backend dumps core */
+ sprintf(conn->errorMessage,"FATAL: unexpected results from the backend, it probably dumped core.");
+ fprintf(stderr, conn->errorMessage);
+ result->resultStatus = PGRES_FATAL_ERROR;
+ return result;
+ break;
+ }
+ if (!done)
+ id = getc(Pfin);
+ } /* while (1) */
+
+ result->resultStatus = PGRES_TUPLES_OK;
+ return result;
+
+makePGresult_badResponse_return:
+ result->resultStatus = PGRES_BAD_RESPONSE;
+ return result;
+
+}
+
+
+
+/*
+ * PQexec
+ * send a query to the backend and package up the result in a Pgresult
+ *
+ * if the query failed, return NULL, conn->errorMessage is set to
+ * a relevant message
+ * if query is successful, a new PGresult is returned
+ * the use is responsible for freeing that structure when done with it
+ *
+ */
+
+PGresult*
+PQexec(PGconn* conn, char* query)
+{
+ PGresult *result;
+ int id, clear;
+ char buffer[MAX_MESSAGE_LEN];
+ char cmdStatus[MAX_MESSAGE_LEN];
+ char pname[MAX_MESSAGE_LEN]; /* portal name */
+ PGnotify *newNotify;
+ FILE *Pfin = conn->Pfin;
+ FILE *Pfout = conn->Pfout;
+ FILE* Pfdebug = conn->Pfdebug;
+
+ pname[0]='\0';
+
+ /*clear the error string */
+ conn->errorMessage[0] = '\0';
+
+ /* check to see if the query string is too long */
+ if (strlen(query) > MAX_MESSAGE_LEN) {
+ sprintf(conn->errorMessage, "PQexec() -- query is too long. Maximum length is %d\n", MAX_MESSAGE_LEN -2 );
+ return NULL;
+ }
+
+ /* the frontend-backend protocol uses 'Q' to designate queries */
+ sprintf(buffer,"Q%s",query);
+
+ /* send the query to the backend; */
+ if (pqPuts(buffer,Pfout, Pfdebug) == 1) {
+ (void) sprintf(conn->errorMessage,
+ "PQexec() -- while sending query: %s\n-- fprintf to Pfout failed: errno=%d\n%s\n",
+ query, errno,strerror(errno));
+ return NULL;
+ }
+
+ /* loop forever because multiple messages, especially NOTICES,
+ can come back from the backend
+ NOTICES are output directly to stderr
+ */
+
+ while (1) {
+
+ /* read the result id */
+ id = pqGetc(Pfin,Pfdebug);
+ if (id == EOF) {
+ /* hmm, no response from the backend-end, that's bad */
+ (void) sprintf(conn->errorMessage,
+ "PQexec() -- No response from backend\n");
+ return (PGresult*)NULL;
+ }
+
+ switch (id) {
+ case 'A':
+ newNotify = (PGnotify*)malloc(sizeof(PGnotify));
+ pqGetInt(&(newNotify->be_pid), 4, Pfin, Pfdebug);
+ pqGets(newNotify->relname, NAMEDATALEN, Pfin, Pfdebug);
+ DLAddTail(conn->notifyList, DLNewElem(newNotify));
+ /* async messages are piggy'ed back on other messages,
+ so we stay in the while loop for other messages */
+ break;
+ case 'C': /* portal query command, no tuples returned */
+ if (pqGets(cmdStatus, MAX_MESSAGE_LEN, Pfin, Pfdebug) == 1) {
+ sprintf(conn->errorMessage,
+ "PQexec() -- query command completed, but return message from backend cannot be read");
+ return (PGresult*)NULL;
+ }
+ else {
+ /*
+ // since backend may produce more than one result for some commands
+ // need to poll until clear
+ // send an empty query down, and keep reading out of the pipe
+ // until an 'I' is received.
+ */
+ clear = 0;
+
+ pqPuts("Q ",Pfout,Pfdebug); /* send an empty query */
+ while (!clear)
+ {
+ if (pqGets(buffer,ERROR_MSG_LENGTH,Pfin,Pfdebug) == 1)
+ clear = 1;
+ clear = (buffer[0] == 'I');
+ }
+ result = makeEmptyPGresult(conn,PGRES_COMMAND_OK);
+ strncpy(result->cmdStatus,cmdStatus, CMDSTATUS_LEN-1);
+ return result;
+ }
+ break;
+ case 'E': /* error return */
+ if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, Pfin, Pfdebug) == 1) {
+ (void) sprintf(conn->errorMessage,
+ "PQexec() -- error return detected from backend, but error message cannot be read");
+ }
+ return (PGresult*)NULL;
+ break;
+ case 'I': /* empty query */
+ /* read the throw away the closing '\0' */
+ {
+ int c;
+ if ((c = pqGetc(Pfin,Pfdebug)) != '\0') {
+ fprintf(stderr,"error!, unexpected character %c following 'I'\n", c);
+ }
+ result = makeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
+ return result;
+ }
+ break;
+ case 'N': /* notices from the backend */
+ if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, Pfin, Pfdebug) == 1) {
+ sprintf(conn->errorMessage,
+ "PQexec() -- error return detected from backend, but error message cannot be read");
+ return (PGresult*)NULL;
+ }
+ else
+ fprintf(stderr,"%s", conn->errorMessage);
+ break;
+ case 'P': /* synchronous (normal) portal */
+ pqGets(pname,MAX_MESSAGE_LEN,Pfin, Pfdebug); /* read in the portal name*/
+ break;
+ case 'T': /* actual tuple results: */
+ return makePGresult(conn, pname);
+ break;
+ case 'D': /* copy command began successfully */
+ return makeEmptyPGresult(conn,PGRES_COPY_IN);
+ break;
+ case 'B': /* copy command began successfully */
+ return makeEmptyPGresult(conn,PGRES_COPY_OUT);
+ break;
+ default:
+ sprintf(conn->errorMessage,
+ "unknown protocol character %c read from backend\n",
+ id);
+ return (PGresult*)NULL;
+ } /* switch */
+} /* while (1)*/
+
+}
+
+/*
+ * PQnotifies
+ * returns a PGnotify* structure of the latest async notification
+ * that has not yet been handled
+ *
+ * returns NULL, if there is currently
+ * no unhandled async notification from the backend
+ *
+ * the CALLER is responsible for FREE'ing the structure returned
+ */
+
+PGnotify*
+PQnotifies(PGconn *conn)
+{
+ Dlelem *e;
+ if (conn->status != CONNECTION_OK)
+ return NULL;
+ /* RemHead returns NULL if list is empy */
+ e = DLRemHead(conn->notifyList);
+ if (e)
+ return (PGnotify*)DLE_VAL(e);
+ else
+ return NULL;
+}
+
+/*
+ * PQgetline - gets a newline-terminated string from the backend.
+ *
+ * Chiefly here so that applications can use "COPY <rel> to stdout"
+ * and read the output string. Returns a null-terminated string in s.
+ *
+ * PQgetline reads up to maxlen-1 characters (like fgets(3)) but strips
+ * the terminating \n (like gets(3)).
+ *
+ * RETURNS:
+ * EOF if it is detected or invalid arguments are given
+ * 0 if EOL is reached (i.e., \n has been read)
+ * (this is required for backward-compatibility -- this
+ * routine used to always return EOF or 0, assuming that
+ * the line ended within maxlen bytes.)
+ * 1 in other cases
+ */
+int
+PQgetline(PGconn *conn, char *s, int maxlen)
+{
+ int c = '\0';
+
+ if (!conn->Pfin || !s || maxlen <= 1)
+ return(EOF);
+
+ for (; maxlen > 1 &&
+ (c = pqGetc(conn->Pfin, conn->Pfdebug)) != '\n' &&
+ c != EOF;
+ --maxlen) {
+ *s++ = c;
+ }
+ *s = '\0';
+
+ if (c == EOF) {
+ return(EOF); /* error -- reached EOF before \n */
+ } else if (c == '\n') {
+ return(0); /* done with this line */
+ }
+ return(1); /* returning a full buffer */
+}
+
+
+/*
+ * PQputline -- sends a string to the backend.
+ *
+ * Chiefly here so that applications can use "COPY <rel> from stdin".
+ *
+ */
+void
+PQputline(PGconn *conn, char *s)
+{
+ if (conn->Pfout) {
+ (void) fputs(s, conn->Pfout);
+ fflush(conn->Pfout);
+ }
+}
+
+/*
+ * PQendcopy
+ * called while waiting for the backend to respond with success/failure
+ * to a "copy".
+ *
+ * RETURNS:
+ * 0 on failure
+ * 1 on success
+ */
+int
+PQendcopy(PGconn *conn)
+{
+ char id;
+ FILE *Pfin = conn->Pfin;
+ FILE* Pfdebug = conn->Pfdebug;
+
+ if ( (id = pqGetc(Pfin,Pfdebug)) > 0)
+ return(0);
+ switch (id) {
+ case 'Z': /* backend finished the copy */
+ return(1);
+ case 'E':
+ case 'N':
+ if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, Pfin, Pfdebug) == 1) {
+ sprintf(conn->errorMessage,
+ "Error return detected from backend, but error message cannot be read");
+ }
+ return(0);
+ break;
+ default:
+ (void) sprintf(conn->errorMessage,
+ "FATAL: PQendcopy: protocol error: id=%x\n",
+ id);
+ fputs(conn->errorMessage, stderr);
+ fprintf(stderr,"resetting connection\n");
+ PQreset(conn);
+ return(0);
+ }
+}
+
+/* simply send out max-length number of filler characters to fp */
+static void
+fill (int length, int max, char filler, FILE *fp)
+{
+ int count;
+ char filltmp[2];
+
+ filltmp[0] = filler;
+ filltmp[1] = 0;
+ count = max - length;
+ while (count-- >= 0)
+ {
+ fprintf(fp, "%s", filltmp);
+ }
+ }
+
+
+/*
+ * PQdisplayTuples()
+ *
+ * a better version of PQprintTuples()
+ * that can optionally do padding of fields with spaces and use different
+ * field separators
+ */
+void
+PQdisplayTuples(PGresult *res,
+ FILE *fp, /* where to send the output */
+ int fillAlign, /* pad the fields with spaces */
+ char *fieldSep, /* field separator */
+ int printHeader, /* display headers? */
+ int quiet
+ )
+{
+#define DEFAULT_FIELD_SEP " "
+
+ char *pager;
+ int i, j;
+ int nFields;
+ int nTuples;
+ int fLength[MAX_FIELDS];
+ int usePipe = 0;
+
+ if (fieldSep == NULL)
+ fieldSep == DEFAULT_FIELD_SEP;
+
+ if (fp == NULL)
+ fp = stdout;
+ if (fp == stdout) {
+ /* try to pipe to the pager program if possible */
+ pager=getenv("PAGER");
+ if (pager != NULL) {
+ fp = popen(pager, "w");
+ if (fp) {
+ usePipe = 1;
+ signal(SIGPIPE, SIG_IGN);
+ } else
+ fp = stdout;
+ }
+ }
+
+ /* Get some useful info about the results */
+ nFields = PQnfields(res);
+ nTuples = PQntuples(res);
+
+ /* Zero the initial field lengths */
+ for (j=0 ; j < nFields; j++) {
+ fLength[j] = strlen(PQfname(res,j));
+ }
+ /* Find the max length of each field in the result */
+ /* will be somewhat time consuming for very large results */
+ if (fillAlign) {
+ for (i=0; i < nTuples; i++) {
+ for (j=0 ; j < nFields; j++) {
+ if (PQgetlength(res,i,j) > fLength[j])
+ fLength[j] = PQgetlength(res,i,j);
+ }
+ }
+ }
+
+ if (printHeader) {
+ /* first, print out the attribute names */
+ for (i=0; i < nFields; i++) {
+ fputs(PQfname(res,i), fp);
+ if (fillAlign)
+ fill (strlen (PQfname(res,i)), fLength[i], ' ', fp);
+ fputs(fieldSep,fp);
+ }
+ fprintf(fp, "\n");
+
+ /* Underline the attribute names */
+ for (i=0; i < nFields; i++) {
+ if (fillAlign)
+ fill (0, fLength[i], '-', fp);
+ fputs(fieldSep,fp);
+ }
+ fprintf(fp, "\n");
+ }
+
+ /* next, print out the instances */
+ for (i=0; i < nTuples; i++) {
+ for (j=0 ; j < nFields; j++) {
+ fprintf(fp, "%s", PQgetvalue(res,i,j));
+ if (fillAlign)
+ fill (strlen (PQgetvalue(res,i,j)), fLength[j], ' ', fp);
+ fputs(fieldSep,fp);
+ }
+ fprintf(fp, "\n");
+ }
+
+ if (!quiet)
+ fprintf (fp, "\nQuery returned %d row%s.\n",PQntuples(res),
+ (PQntuples(res) == 1) ? "" : "s");
+
+ fflush(fp);
+ if (usePipe) {
+ pclose(fp);
+ signal(SIGPIPE, SIG_DFL);
+ }
+}
+
+
+
+/*
+ * PQprintTuples()
+ *
+ * This is the routine that prints out the tuples that
+ * are returned from the backend.
+ * Right now all columns are of fixed length,
+ * this should be changed to allow wrap around for
+ * tuples values that are wider.
+ */
+void
+PQprintTuples(PGresult *res,
+ FILE* fout, /* output stream */
+ int PrintAttNames,/* print attribute names or not*/
+ int TerseOutput, /* delimiter bars or not?*/
+ int colWidth /* width of column, if 0, use variable width */
+ )
+{
+ int nFields;
+ int nTups;
+ int i,j;
+ char formatString[80];
+
+ char *tborder = NULL;
+
+ nFields = PQnfields(res);
+ nTups = PQntuples(res);
+
+ if (colWidth > 0) {
+ sprintf(formatString,"%%s %%-%ds",colWidth);
+ } else
+ sprintf(formatString,"%%s %%s");
+
+ if ( nFields > 0 ) { /* only print tuples with at least 1 field. */
+
+ if (!TerseOutput)
+ {
+ int width;
+ width = nFields * 14;
+ tborder = malloc (width+1);
+ for (i = 0; i <= width; i++)
+ tborder[i] = '-';
+ tborder[i] = '\0';
+ fprintf(fout,"%s\n",tborder);
+ }
+
+ for (i=0; i < nFields; i++) {
+ if (PrintAttNames) {
+ fprintf(fout,formatString,
+ TerseOutput ? "" : "|",
+ PQfname(res, i));
+ }
+ }
+
+ if (PrintAttNames) {
+ if (TerseOutput)
+ fprintf(fout,"\n");
+ else
+ fprintf(fout, "|\n%s\n",tborder);
+ }
+
+ for (i = 0; i < nTups; i++) {
+ for (j = 0; j < nFields; j++) {
+ char *pval = PQgetvalue(res,i,j);
+ fprintf(fout, formatString,
+ TerseOutput ? "" : "|",
+ pval ? pval : "");
+ }
+ if (TerseOutput)
+ fprintf(fout,"\n");
+ else
+ fprintf(fout, "|\n%s\n",tborder);
+ }
+ }
+}
+
+
+/* ----------------
+ * PQfn - Send a function call to the POSTGRES backend.
+ *
+ * conn : backend connection
+ * fnid : function id
+ * result_buf : pointer to result buffer (&int if integer)
+ * result_len : length of return value.
+ * actual_result_len: actual length returned. (differs from result_len
+ * for varlena structures.)
+ * result_type : If the result is an integer, this must be 1,
+ * otherwise this should be 0
+ * args : pointer to a NULL terminated arg array.
+ * (length, if integer, and result-pointer)
+ * nargs : # of arguments in args array.
+ *
+ * RETURNS
+ * NULL on failure. PQerrormsg will be set.
+ * "G" if there is a return value.
+ * "V" if there is no return value.
+ * ----------------
+ */
+
+PGresult*
+PQfn(PGconn *conn,
+ int fnid,
+ int *result_buf,
+ int *actual_result_len,
+ int result_is_int,
+ PQArgBlock *args,
+ int nargs)
+{
+ FILE *Pfin = conn->Pfin;
+ FILE *Pfout = conn->Pfout;
+ FILE* Pfdebug = conn->Pfdebug;
+ int id;
+ int i;
+
+ /* clear the error string */
+ conn->errorMessage[0] = '\0';
+
+ pqPuts("F ",Pfout,Pfdebug); /* function */
+ pqPutInt(fnid, 4, Pfout, Pfdebug); /* function id */
+ pqPutInt(nargs, 4, Pfout, Pfdebug); /* # of args */
+
+ for (i = 0; i < nargs; ++i) { /* len.int4 + contents */
+ pqPutInt(args[i].len, 4, Pfout, Pfdebug);
+ if (args[i].isint) {
+ pqPutInt(args[i].u.integer, 4, Pfout, Pfdebug);
+ } else {
+ pqPutnchar((char *)args[i].u.ptr, args[i].len, Pfout, Pfdebug);
+ }
+ }
+ pqFlush(Pfout, Pfdebug);
+
+ id = pqGetc(Pfin, Pfdebug);
+ if (id != 'V') {
+ if (id == 'E') {
+ pqGets(conn->errorMessage,ERROR_MSG_LENGTH,Pfin,Pfdebug);
+ } else
+ sprintf(conn->errorMessage,
+ "PQfn: expected a 'V' from the backend. Got '%c' instead",
+ id);
+ return makeEmptyPGresult(conn,PGRES_FATAL_ERROR);
+ }
+
+ id = pqGetc(Pfin, Pfdebug);
+ for (;;) {
+ int c;
+ switch (id) {
+ case 'G': /* function returned properly */
+ pqGetInt(actual_result_len,4,Pfin,Pfdebug);
+ if (result_is_int) {
+ pqGetInt(result_buf,4,Pfin,Pfdebug);
+ } else {
+ pqGetnchar((char *) result_buf, *actual_result_len,
+ Pfin, Pfdebug);
+ }
+ c = pqGetc(Pfin, Pfdebug); /* get the last '0'*/
+ return makeEmptyPGresult(conn,PGRES_COMMAND_OK);
+ case 'E':
+ sprintf(conn->errorMessage,
+ "PQfn: returned an error");
+ return makeEmptyPGresult(conn,PGRES_FATAL_ERROR);
+ case 'N':
+ /* print notice and go back to processing return values */
+ if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, Pfin, Pfdebug) == 1) {
+ sprintf(conn->errorMessage,
+ "Notice return detected from backend, but error message cannot be read");
+ } else
+ fprintf(stderr, "%s\n", conn->errorMessage);
+ /* keep iterating */
+ break;
+ case '0': /* no return value */
+ return makeEmptyPGresult(conn,PGRES_COMMAND_OK);
+ default:
+ /* The backend violates the protocol. */
+ sprintf(conn->errorMessage,
+ "FATAL: PQfn: protocol error: id=%x\n", id);
+ return makeEmptyPGresult(conn,PGRES_FATAL_ERROR);
+ }
+ }
+}
+
+
+
+
+/* ====== accessor funcs for PGresult ======== */
+
+ExecStatusType
+PQresultStatus(PGresult* res)
+{
+ return res->resultStatus;
+}
+
+int
+PQntuples(PGresult *res)
+{
+ return res->ntups;
+}
+
+int
+PQnfields(PGresult *res)
+{
+ return res->numAttributes;
+}
+
+/*
+ returns NULL if the field_num is invalid
+*/
+char*
+PQfname(PGresult *res, int field_num)
+{
+ if (field_num > (res->numAttributes - 1)) {
+ fprintf(stderr,
+ "PQfname: ERROR! name of field %d(of %d) is not available",
+ field_num, res->numAttributes -1);
+ return NULL;
+ }
+ if (res->attDescs) {
+ return res->attDescs[field_num].name;
+ } else
+ return NULL;
+}
+
+/*
+ returns -1 on a bad field name
+*/
+int
+PQfnumber(PGresult *res, char* field_name)
+{
+ int i;
+
+ if (field_name == NULL ||
+ field_name[0] == '\0' ||
+ res->attDescs == NULL)
+ return -1;
+
+ for (i=0;i<res->numAttributes;i++) {
+ if ( strcmp(field_name, res->attDescs[i].name) == 0 )
+ return i;
+ }
+ return -1;
+
+}
+
+Oid
+PQftype(PGresult *res, int field_num)
+{
+ if (field_num > (res->numAttributes - 1)) {
+ fprintf(stderr,
+ "PQftype: ERROR! type of field %d(of %d) is not available",
+ field_num, res->numAttributes -1);
+ }
+ if (res->attDescs) {
+ return res->attDescs[field_num].adtid;
+ } else
+ return InvalidOid;
+}
+
+int2
+PQfsize(PGresult *res, int field_num)
+{
+ if (field_num > (res->numAttributes - 1)) {
+ fprintf(stderr,
+ "PQfsize: ERROR! size of field %d(of %d) is not available",
+ field_num, res->numAttributes -1);
+ }
+ if (res->attDescs) {
+ return res->attDescs[field_num].adtsize;
+ } else
+ return 0;
+}
+
+char* PQcmdStatus(PGresult *res) {
+ return res->cmdStatus;
+}
+
+/*
+ PQoidStatus -
+ if the last command was an INSERT, return the oid string
+ if not, return ""
+*/
+char* PQoidStatus(PGresult *res) {
+ if (!res->cmdStatus)
+ return "";
+
+ if (strncmp(res->cmdStatus, "INSERT",6) == 0) {
+ return res->cmdStatus+7;
+ } else
+ return "";
+}
+
+/*
+ PQgetvalue:
+ return the attribute value of field 'field_num' of
+ tuple 'tup_num'
+
+ If res is binary, then the value returned is NOT a null-terminated
+ ASCII string, but the binary representation in the server's native
+ format.
+
+ if res is not binary, a null-terminated ASCII string is returned.
+*/
+char*
+PQgetvalue(PGresult *res, int tup_num, int field_num)
+{
+ if (tup_num > (res->ntups - 1) ||
+ field_num > (res->numAttributes - 1)) {
+ fprintf(stderr,
+ "PQgetvalue: ERROR! field %d(of %d) of tuple %d(of %d) is not available",
+ field_num, res->numAttributes - 1, tup_num, res->ntups);
+ }
+
+
+ return res->tuples[tup_num][field_num].value;
+}
+
+/* PQgetlength:
+ returns the length of a field value in bytes. If res is binary,
+ i.e. a result of a binary portal, then the length returned does
+ NOT include the size field of the varlena.
+*/
+int
+PQgetlength(PGresult *res, int tup_num, int field_num)
+{
+ if (tup_num > (res->ntups - 1 )||
+ field_num > (res->numAttributes - 1)) {
+ fprintf(stderr,
+ "PQgetlength: ERROR! field %d(of %d) of tuple %d(of %d) is not available",
+ field_num, res->numAttributes - 1, tup_num, res->ntups);
+ }
+
+ return res->tuples[tup_num][field_num].len;
+ }
diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c
new file mode 100644
index 00000000000..c0c60d4a781
--- /dev/null
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -0,0 +1,381 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-lobj.c--
+ * Front-end large object interface
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-lobj.c,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "postgres.h"
+#include "libpq-fe.h"
+#include "obj/fmgr.h"
+#include "libpq/libpq-fs.h"
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+
+#define LO_BUFSIZE 1024
+
+/*
+ * lo_open
+ * opens an existing large object
+ *
+ * returns the file descriptor for use in later lo_* calls
+ * return -1 upon failure.
+ */
+int
+lo_open(PGconn* conn, Oid lobjId, int mode)
+{
+ int fd;
+ int result_len;
+ PQArgBlock argv[2];
+ PGresult *res;
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = lobjId;
+
+ argv[1].isint = 1;
+ argv[1].len = 4;
+ argv[1].u.integer = mode;
+
+ res = PQfn(conn, F_LO_OPEN,&fd,&result_len,1,argv,2);
+ if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+ PQclear(res);
+
+ /* have to do this to reset offset in shared fd cache */
+ /* but only if fd is valid */
+ if (fd >= 0 && lo_lseek(conn, fd, 0L, SEEK_SET) < 0)
+ return -1;
+ return fd;
+ } else
+ return -1;
+}
+
+/*
+ * lo_close
+ * closes an existing large object
+ *
+ * returns 0 upon success
+ * returns -1 upon failure.
+ */
+int
+lo_close(PGconn *conn, int fd)
+{
+ PQArgBlock argv[1];
+ PGresult *res;
+ int retval;
+ int result_len;
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = fd;
+ res = PQfn(conn, F_LO_CLOSE,&retval,&result_len,1,argv,1);
+ if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+ PQclear(res);
+ return retval;
+ } else
+ return -1;
+}
+
+/*
+ * lo_read
+ * read len bytes of the large object into buf
+ *
+ * returns the length of bytes read.
+ * the CALLER must have allocated enough space to hold the result returned
+ */
+
+int
+lo_read(PGconn *conn, int fd, char *buf, int len)
+{
+ PQArgBlock argv[2];
+ PGresult *res;
+ int result_len;
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = fd;
+
+ argv[1].isint = 1;
+ argv[1].len = 4;
+ argv[1].u.integer = len;
+
+ res = PQfn(conn, F_LOREAD,(int*)buf,&result_len,0,argv,2);
+ if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+ PQclear(res);
+ return result_len;
+ } else
+ return -1;
+}
+
+/*
+ * lo_write
+ * write len bytes of buf into the large object fd
+ *
+ */
+int
+lo_write(PGconn *conn, int fd, char *buf, int len)
+{
+ PQArgBlock argv[2];
+ PGresult *res;
+ int result_len;
+ int retval;
+
+ if (len <= 0)
+ return 0;
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = fd;
+
+ argv[1].isint = 0;
+ argv[1].len = len;
+ argv[1].u.ptr = (int*)buf;
+
+ res = PQfn(conn, F_LOWRITE,&retval,&result_len,1,argv,2);
+ if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+ PQclear(res);
+ return retval;
+ } else
+ return -1;
+}
+
+/*
+ * lo_lseek
+ * change the current read or write location on a large object
+ * currently, only L_SET is a legal value for whence
+ *
+ */
+
+int
+lo_lseek(PGconn *conn, int fd, int offset, int whence)
+{
+ PQArgBlock argv[3];
+ PGresult *res;
+ int retval;
+ int result_len;
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = fd;
+
+ argv[1].isint = 1;
+ argv[1].len = 4;
+ argv[1].u.integer = offset;
+
+ argv[2].isint = 1;
+ argv[2].len = 4;
+ argv[2].u.integer = whence;
+
+ res = PQfn(conn, F_LO_LSEEK,&retval,&result_len,1,argv,3);
+ if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+ PQclear(res);
+ return retval;
+ } else
+ return -1;
+}
+
+/*
+ * lo_creat
+ * create a new large object
+ * the mode is a bitmask describing different attributes of the new object
+ *
+ * returns the oid of the large object created or
+ * InvalidOid upon failure
+ */
+
+Oid
+lo_creat(PGconn *conn, int mode)
+{
+ PQArgBlock argv[1];
+ PGresult *res;
+ int retval;
+ int result_len;
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = mode;
+ res = PQfn(conn, F_LO_CREAT,&retval,&result_len,1,argv,1);
+ if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+ PQclear(res);
+ return (Oid)retval;
+ } else
+ return InvalidOid;
+}
+
+
+/*
+ * lo_tell
+ * returns the current seek location of the large object
+ *
+ */
+
+int
+lo_tell(PGconn *conn, int fd)
+{
+ int retval;
+ PQArgBlock argv[1];
+ PGresult *res;
+ int result_len;
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = fd;
+
+ res = PQfn(conn, F_LO_TELL,&retval,&result_len,1,argv,1);
+ if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+ PQclear(res);
+ return retval;
+ } else
+ return -1;
+}
+
+/*
+ * lo_unlink
+ * delete a file
+ *
+ */
+
+int
+lo_unlink(PGconn *conn, Oid lobjId)
+{
+ PQArgBlock argv[1];
+ PGresult *res;
+ int result_len;
+ int retval;
+
+ argv[0].isint = 1;
+ argv[0].len = 4;
+ argv[0].u.integer = lobjId;
+
+ res = PQfn(conn, F_LO_UNLINK,&retval,&result_len,1,argv,1);
+ if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+ PQclear(res);
+ return retval;
+ } else
+ return -1;
+}
+
+/*
+ * lo_import -
+ * imports a file as an (inversion) large object.
+ * returns the oid of that object upon success,
+ * returns InvalidOid upon failure
+ *
+ */
+
+Oid
+lo_import(PGconn *conn, char* filename)
+{
+ int fd;
+ int nbytes, tmp;
+ char buf[LO_BUFSIZE];
+ Oid lobjOid;
+ int lobj;
+
+ /*
+ * open the file to be read in
+ */
+ fd = open(filename, O_RDONLY, 0666);
+ if (fd < 0) { /* error */
+ sprintf(conn->errorMessage,
+ "lo_import: can't open unix file\"%s\"\n", filename);
+ return InvalidOid;
+ }
+
+ /*
+ * create an inversion "object"
+ */
+ lobjOid = lo_creat(conn, INV_READ|INV_WRITE);
+ if (lobjOid == InvalidOid) {
+ sprintf(conn->errorMessage,
+ "lo_import: can't create inv object for \"%s\"", filename);
+ return InvalidOid;
+ }
+
+ lobj = lo_open(conn, lobjOid, INV_WRITE);
+ if (lobj == -1) {
+ sprintf(conn->errorMessage,
+ "lo_import: could not open inv object oid %d",lobjOid);
+ return InvalidOid;
+ }
+ /*
+ * read in from the Unix file and write to the inversion file
+ */
+ while ((nbytes = read(fd, buf, LO_BUFSIZE)) > 0) {
+ tmp = lo_write(conn,lobj, buf, nbytes);
+ if (tmp < nbytes) {
+ sprintf(conn->errorMessage,
+ "lo_import: error while reading \"%s\"",filename);
+ return InvalidOid;
+ }
+ }
+
+ (void) close(fd);
+ (void) lo_close(conn, lobj);
+
+ return lobjOid;
+}
+
+/*
+ * lo_export -
+ * exports an (inversion) large object.
+ * returns -1 upon failure, 1 otherwise
+ */
+int
+lo_export(PGconn *conn, Oid lobjId, char *filename)
+{
+ int fd;
+ int nbytes, tmp;
+ char buf[LO_BUFSIZE];
+ int lobj;
+
+ /*
+ * create an inversion "object"
+ */
+ lobj = lo_open(conn, lobjId, INV_READ);
+ if (lobj == -1) {
+ sprintf(conn->errorMessage,
+ "lo_export: can't open inv object %d",lobjId);
+ return -1;
+ }
+
+ /*
+ * open the file to be written to
+ */
+ fd = open(filename, O_CREAT|O_WRONLY, 0666);
+ if (fd < 0) { /* error */
+ sprintf(conn->errorMessage,
+ "lo_export: can't open unix file\"%s\"",filename);
+ return 0;
+ }
+
+ /*
+ * read in from the Unix file and write to the inversion file
+ */
+ while ((nbytes = lo_read(conn, lobj, buf, LO_BUFSIZE)) > 0) {
+ tmp = write(fd, buf, nbytes);
+ if (tmp < nbytes) {
+ sprintf(conn->errorMessage,
+ "lo_export: error while writing \"%s\"",
+ filename);
+ return -1;
+ }
+ }
+
+ (void) lo_close(conn,lobj);
+ (void) close(fd);
+
+ return 1;
+}
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
new file mode 100644
index 00000000000..c8b703b9d33
--- /dev/null
+++ b/src/interfaces/libpq/fe-misc.c
@@ -0,0 +1,193 @@
+/*-------------------------------------------------------------------------
+ *
+ * FILE
+ * fe-misc.c
+ *
+ * DESCRIPTION
+ * miscellaneous useful functions
+ * these routines are analogous to the ones in libpq/pqcomm.c
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/* pqGetc:
+ get a character from stream f
+
+ if debug is set, also echo the character fetched
+*/
+int
+pqGetc(FILE* fin, FILE* debug)
+{
+ int c;
+
+ c = getc(fin);
+ if (debug && c != EOF)
+ putc(c,debug);
+ return c;
+}
+
+/* pqPutnchar:
+ send a string of exactly len length into stream f
+
+ returns 1 if there was an error, 0 otherwise.
+*/
+int
+pqPutnchar(char* s, int len, FILE *f, FILE *debug)
+{
+ int status;
+
+ if (f == NULL)
+ return 1;
+
+ while (len--) {
+ status = fputc(*s,f);
+ if (debug)
+ fputc(*s,debug);
+ s++;
+ if (status == EOF)
+ return 1;
+ }
+ return 0;
+}
+
+/* pqGetnchar:
+ get a string of exactly len length from stream f
+*/
+int
+pqGetnchar(char* s, int len, FILE *f, FILE *debug)
+{
+ int c;
+
+ if (f == NULL)
+ return 1;
+
+ while (len-- && (c = getc(f)) != EOF)
+ *s++ = c;
+ *s = '\0';
+
+ if (debug) {
+ fputs(s,debug);
+ }
+ return 0;
+}
+
+/* pqGets:
+ get a string of up to length len from stream f
+*/
+int
+pqGets(char* s, int len, FILE *f, FILE *debug)
+{
+ int c;
+
+ if (f == NULL)
+ return 1;
+
+ while (len-- && (c = getc(f)) != EOF && c)
+ *s++ = c;
+ *s = '\0';
+
+ if (debug) {
+ fputs(s,debug);
+ }
+ return 0;
+}
+
+
+/* pgPutInt
+ send an integer of up to 4 bytesto the file stream
+ do this one byte at at time.
+ This insures that machines with different ENDIANness can talk to each other
+ get a n-byte integer from the stream into result
+ returns 0 if successful, 1 otherwise
+*/
+int
+pqPutInt(int i, int bytes, FILE* f, FILE *debug)
+{
+ int status;
+
+ if (bytes > 4)
+ bytes = 4;
+
+ while (bytes--) {
+ status = fputc(i & 0xff, f);
+ if (debug)
+ fputc(i & 0xff, debug);
+ i >>= 8;
+ if (status == EOF) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* pgGetInt
+ reconstructs the integer one byte at a time.
+ This insures that machines with different ENDIANness can talk to each other
+ get a n-byte integer from the stream into result
+ returns 0 if successful
+*/
+int
+pqGetInt(int* result, int bytes, FILE* f, FILE *debug)
+{
+ int c;
+ int p;
+ int n;
+
+ if (f == NULL)
+ return 1;
+
+ p = 0;
+ n = 0;
+ while (bytes && (c = getc(f)) != EOF)
+ {
+ n |= (c & 0xff) << p;
+ p += 8;
+ bytes--;
+ }
+
+ if (bytes != 0)
+ return 1;
+
+ *result = n;
+ if (debug)
+ fprintf(debug,"%d",*result);
+ return 0;
+}
+
+
+int
+pqPuts(char* s, FILE *f, FILE *debug)
+{
+ if (f == NULL)
+ return 1;
+
+ if (fputs(s,f) == EOF)
+ return 1;
+
+ fputc('\0',f); /* important to send an ending EOF since backend expects it */
+ fflush(f);
+
+ if (debug) {
+ fputs(s,debug);
+ }
+ return 0;
+}
+
+
+void
+pqFlush(FILE *f, FILE *debug)
+{
+ if (f)
+ fflush(f);
+ if (debug)
+ fflush(debug);
+}
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
new file mode 100644
index 00000000000..af3413b4375
--- /dev/null
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -0,0 +1,251 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpq-fe.h--
+ * This file contains definitions for structures and
+ * externs for functions used by frontend postgres applications.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: libpq-fe.h,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef LIBPQ_FE_H
+#define LIBPQ_FE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ----------------
+ * include stuff common to fe and be
+ * ----------------
+ */
+/* #include "libpq/libpq.h" */
+#include "libpq/pqcomm.h"
+#include "lib/dllist.h"
+
+typedef enum {CONNECTION_OK,
+ CONNECTION_BAD} ConnStatusType;
+
+typedef enum {
+ PGRES_EMPTY_QUERY = 0,
+ PGRES_COMMAND_OK, /* a query command that doesn't return */
+ /* anything was executed properly by the backend */
+ PGRES_TUPLES_OK, /* a query command that returns tuples */
+ /* was executed properly by the backend, PGresult */
+ /* contains the resulttuples */
+ PGRES_COPY_OUT,
+ PGRES_COPY_IN,
+ PGRES_BAD_RESPONSE, /* an unexpected response was recv'd from the backend */
+ PGRES_NONFATAL_ERROR,
+ PGRES_FATAL_ERROR
+
+} ExecStatusType;
+
+/* string descriptions of the ExecStatusTypes */
+extern char* pgresStatus[];
+
+/*
+ * POSTGRES backend dependent Constants.
+ */
+
+/* ERROR_MSG_LENGTH should really be the same as ELOG_MAXLEN in utils/elog.h*/
+#define ERROR_MSG_LENGTH 4096
+#define COMMAND_LENGTH 20
+#define REMARK_LENGTH 80
+#define PORTAL_NAME_LENGTH 16
+
+/* ----------------
+ * PQArgBlock --
+ * Information (pointer to array of this structure) required
+ * for the PQfn() call.
+ * ----------------
+ */
+typedef struct {
+ int len;
+ int isint;
+ union {
+ int *ptr; /* can't use void (dec compiler barfs) */
+ int integer;
+ } u;
+} PQArgBlock;
+
+typedef struct pgresAttDesc {
+ char* name; /* type name */
+ Oid adtid; /* type id */
+ int2 adtsize; /* type size */
+} PGresAttDesc;
+
+/* use char* for Attribute values,
+ ASCII tuples are guaranteed to be null-terminated
+ For binary tuples, the first four bytes of the value is the size,
+ and the bytes afterwards are the value. The binary value is
+ not guaranteed to be null-terminated. In fact, it can have embedded nulls*/
+typedef struct pgresAttValue {
+ int len; /* length in bytes of the value */
+ char *value; /* actual value */
+} PGresAttValue;
+
+typedef struct pgNotify {
+ char relname[NAMEDATALEN]; /* name of relation containing data */
+ int be_pid; /* process id of backend */
+} PGnotify;
+
+/* PGconn encapsulates a connection to the backend */
+typedef struct pg_conn{
+ char *pghost; /* the machine on which the server is running */
+ char *pgtty; /* tty on which the backend messages is displayed */
+ char *pgport; /* the communication port with the backend */
+ char *pgoptions; /* options to start the backend with */
+ char *dbName; /* database name */
+ ConnStatusType status;
+ char errorMessage[ERROR_MSG_LENGTH];
+ /* pipes for be/fe communication */
+ FILE *Pfin;
+ FILE *Pfout;
+ FILE *Pfdebug;
+ void *port; /* really a Port* */
+ int asyncNotifyWaiting;
+ Dllist* notifyList;
+} PGconn;
+
+#define CMDSTATUS_LEN 40
+
+/* PGresult encapsulates the result of a query */
+/* unlike the old libpq, we assume that queries only return in one group */
+typedef struct pg_result{
+ int ntups;
+ int numAttributes;
+ PGresAttDesc *attDescs;
+ PGresAttValue* *tuples; /* each PGresTuple is an array of PGresAttValue's */
+ int tupArrSize; /* size of tuples array allocated */
+ ExecStatusType resultStatus;
+ char cmdStatus[CMDSTATUS_LEN]; /* cmd status from the last insert query*/
+ int binary; /* binary tuple values if binary == 1, otherwise ASCII */
+ PGconn* conn;
+} PGresult;
+
+
+/* === in fe-connect.c === */
+ /* make a new client connection to the backend */
+extern PGconn* PQsetdb(char* pghost, char* pgport, char* pgoptions,
+ char* pgtty, char* dbName);
+ /* close the current connection and free the PGconn data structure */
+extern void PQfinish(PGconn* conn);
+ /* close the current connection and restablish a new one with the same
+ parameters */
+extern void PQreset(PGconn* conn);
+
+extern char* PQdb(PGconn* conn);
+extern char* PQhost(PGconn* conn);
+extern char* PQoptions(PGconn* conn);
+extern char* PQport(PGconn* conn);
+extern char* PQtty(PGconn* conn);
+extern ConnStatusType PQstatus(PGconn* conn);
+extern char* PQerrorMessage(PGconn* conn);
+extern void PQtrace(PGconn *conn, FILE* debug_port);
+extern void PQuntrace(PGconn *conn);
+
+/* === in fe-exec.c === */
+extern PGresult* PQexec(PGconn* conn, char* query);
+extern int PQgetline(PGconn *conn, char* string, int length);
+extern int PQendcopy(PGconn *conn);
+extern void PQputline(PGconn *conn, char* string);
+extern ExecStatusType PQresultStatus(PGresult* res);
+extern int PQntuples(PGresult *res);
+extern int PQnfields(PGresult *res);
+extern char* PQfname(PGresult *res, int field_num);
+extern int PQfnumber(PGresult *res, char* field_name);
+extern Oid PQftype(PGresult *res, int field_num);
+extern int2 PQfsize(PGresult *res, int field_num);
+extern char* PQcmdStatus(PGresult *res);
+extern char* PQoidStatus(PGresult *res);
+extern char* PQgetvalue(PGresult *res, int tup_num, int field_num);
+extern int PQgetlength(PGresult *res, int tup_num, int field_num);
+extern void PQclear(PGresult* res);
+/* PQdisplayTuples() is a better version of PQprintTuples() */
+extern void PQdisplayTuples(PGresult *res,
+ FILE *fp, /* where to send the output */
+ int fillAlign, /* pad the fields with spaces */
+ char *fieldSep, /* field separator */
+ int printHeader, /* display headers? */
+ int quiet);
+extern void PQprintTuples(PGresult* res,
+ FILE* fout, /* output stream */
+ int printAttName,/* print attribute names or not*/
+ int terseOutput, /* delimiter bars or not?*/
+ int width /* width of column,
+ if 0, use variable width */
+ );
+extern PGnotify* PQnotifies(PGconn *conn);
+extern PGresult* PQfn(PGconn* conn,
+ int fnid,
+ int *result_buf,
+ int *result_len,
+ int result_is_int,
+ PQArgBlock *args,
+ int nargs);
+/* === in fe-auth.c === */
+extern MsgType fe_getauthsvc(char* PQerrormsg);
+extern void fe_setauthsvc(char *name, char* PQerrormsg);
+extern char *fe_getauthname(char* PQerrormsg);
+
+/* === in fe-misc.c === */
+/* pqGets and pqPuts gets and sends strings to the file stream
+ returns 0 if successful
+ if debug is non-null, debugging output is sent to that stream
+*/
+extern int pqGets(char* s, int maxlen, FILE* stream, FILE* debug);
+extern int pqGetnchar(char* s, int maxlen, FILE* stream, FILE* debug);
+extern int pqPutnchar(char* s, int maxlen, FILE* stream, FILE* debug);
+extern int pqPuts(char* s, FILE* stream, FILE* debug );
+extern int pqGetc(FILE* stream, FILE *debug);
+/* get a n-byte integer from the stream into result */
+/* returns 0 if successful */
+extern int pqGetInt(int* result, int bytes, FILE* stream, FILE *debug );
+/* put a n-byte integer into the stream */
+/* returns 0 if successful */
+extern int pqPutInt(int n, int bytes, FILE* stream, FILE *debug );
+extern void pqFlush(FILE* stream, FILE* debug);
+
+/* === in fe-lobj.c === */
+int lo_open(PGconn* conn, Oid lobjId, int mode);
+int lo_close(PGconn *conn, int fd);
+int lo_read(PGconn *conn, int fd, char *buf, int len);
+int lo_write(PGconn *conn, int fd, char *buf, int len);
+int lo_lseek(PGconn *conn, int fd, int offset, int whence);
+Oid lo_creat(PGconn *conn, int mode);
+int lo_tell(PGconn *conn, int fd);
+int lo_unlink(PGconn *conn, Oid lobjId);
+Oid lo_import(PGconn *conn, char *filename);
+int lo_export(PGconn *conn, Oid lobjId, char *filename);
+/* max length of message to send */
+#define MAX_MESSAGE_LEN 8193
+
+/* maximum number of fields in a tuple */
+#define BYTELEN 8
+#define MAX_FIELDS 512
+
+/* fall back options if they are not specified by arguments or defined
+ by environment variables */
+#define DefaultHost "localhost"
+#define DefaultTty ""
+#define DefaultOption ""
+
+typedef void *TUPLE;
+#define palloc malloc
+#define pfree free
+
+#if defined(PORTNAME_sparc)
+extern char *sys_errlist[];
+#define strerror(A) (sys_errlist[(A)])
+#endif /* PORTNAME_sparc */
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* LIBPQ_FE_H */
+
diff --git a/src/interfaces/libpq/pg_hba b/src/interfaces/libpq/pg_hba
new file mode 100644
index 00000000000..22a83beb091
--- /dev/null
+++ b/src/interfaces/libpq/pg_hba
@@ -0,0 +1,13 @@
+#
+# Example config file for Postgres95 host based access
+#
+# Lines starting with "all" apply to all databases. Otherwise the first
+# column has to match the name of the database being connected to. Up to
+# ten config lines can apply to each database. Mask specifies bits that
+# aren't counted. After those bits are taken out, the connection address
+# must match the address in the middle column.
+#
+# <name> <address> <mask>
+#
+all 127.0.0.1 0.0.0.0
+
diff --git a/src/interfaces/libpq/pqsignal.c b/src/interfaces/libpq/pqsignal.c
new file mode 100644
index 00000000000..638c494eda7
--- /dev/null
+++ b/src/interfaces/libpq/pqsignal.c
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------------------------
+ *
+ * pqsignal.c--
+ * reliable BSD-style signal(2) routine stolen from RWW who stole it
+ * from Stevens...
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/pqsignal.c,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $
+ *
+ * NOTES
+ * This shouldn't be in libpq, but the monitor and some other
+ * things need it...
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "libpq/pqsignal.h"
+
+pqsigfunc
+pqsignal(int signo, pqsigfunc func)
+{
+#if defined(USE_POSIX_SIGNALS)
+ struct sigaction act, oact;
+
+ act.sa_handler = func;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ if (signo != SIGALRM) {
+ act.sa_flags |= SA_RESTART;
+ }
+ if (sigaction(signo, &act, &oact) < 0)
+ return(SIG_ERR);
+ return(oact.sa_handler);
+#else /* !USE_POSIX_SIGNALS */
+ exit(1); /* this should never be reached, pqsignal should only
+ be called if USE_POSIX_SIGNALS is true*/
+#endif /* !USE_POSIX_SIGNALS */
+}
diff --git a/src/interfaces/libpq/pqsignal.h b/src/interfaces/libpq/pqsignal.h
new file mode 100644
index 00000000000..77d01b0a8eb
--- /dev/null
+++ b/src/interfaces/libpq/pqsignal.h
@@ -0,0 +1,32 @@
+/*-------------------------------------------------------------------------
+ *
+ * pqsignal.h--
+ * prototypes for the reliable BSD-style signal(2) routine.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: pqsignal.h,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $
+ *
+ * NOTES
+ * This shouldn't be in libpq, but the monitor and some other
+ * things need it...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PQSIGNAL_H
+#define PQSIGNAL_H
+
+#include <signal.h>
+
+#include "c.h"
+
+typedef void (*pqsigfunc)(int);
+
+extern pqsigfunc pqsignal(int signo, pqsigfunc func);
+
+#if defined(USE_POSIX_SIGNALS)
+#define signal(signo, handler) pqsignal(signo, (pqsigfunc)(handler))
+#endif /* USE_POSIX_SIGNALS */
+
+#endif /* PQSIGNAL_H */