Implementing IPv6 ... - Mailing list pgsql-odbc

From Hans-Jürgen Schönig
Subject Implementing IPv6 ...
Date
Msg-id 42FBAF67.6010305@cybertec.at
Whole thread Raw
List pgsql-odbc
Folks,

This patch implements IPv6 for some widespread platforms.
It has been tested on Linux. Maybe some people will have the time to
test some other platforms.

    Best regards,

        Hans


--
Cybertec Geschwinde & Schönig GmbH
Schöngrabern 134; A-2020 Hollabrunn
Tel: +43/1/205 10 35 / 340
www.postgresql.at, www.cybertec.at

 This patch implements IPv6 for PostgreSQL ODBC.

 The patch is __without__ ./configure changes, there's new configure.in stuff only.
 It means you have to call "autoconf" after the patch command.

     patch -p1 < psqlodbc-08.00.0102-ipv6.patch
     autoconf
     ./configure --enable-pthreads --with-unixodbc
     make

 /etc/odbc.ini (IPv6 localhost)

    [PostgreSQL]
    Description         = Test to Postgres
    Driver              = /usr/lib/psqlodbc.so
    Database            = test
    Servername          = ::ffff:127.0.0.1
    UserName            = you
    Password            =

 -- 10-Aug-2005     Hans-Jurgen Schonig <hs@cybertec.at>

diff -B -u -r --new-file psqlodbc-08.00.0102.old/config.h.in psqlodbc-08.00.0102.new/config.h.in
--- psqlodbc-08.00.0102.old/config.h.in    2005-08-02 08:58:09.000000000 +0200
+++ psqlodbc-08.00.0102.new/config.h.in    2005-08-10 14:59:14.000000000 +0200
@@ -36,6 +36,15 @@
 /* Define to 1 if you have the `socket' library (-lsocket). */
 #undef HAVE_LIBSOCKET

+/* Define to 1 if you have the `getaddrinfo' function. */
+#undef HAVE_GETADDRINFO
+
+/* Define to 1 if you have support for IPv6. */
+#undef HAVE_IPV6
+
+/* Define to 1 if you have support for sockaddr absraction */
+#undef HAVE_STRUCT_SOCKADDR_STORAGE
+
 /* Define to 1 if you have the <locale.h> header file. */
 #undef HAVE_LOCALE_H

diff -B -u -r --new-file psqlodbc-08.00.0102.old/configure.ac psqlodbc-08.00.0102.new/configure.ac
--- psqlodbc-08.00.0102.old/configure.ac    2005-08-02 09:38:03.000000000 +0200
+++ psqlodbc-08.00.0102.new/configure.ac    2005-08-10 15:06:21.000000000 +0200
@@ -44,6 +44,24 @@
   AC_DEFINE(WITH_IODBC, 1, [Define to 1 to build with iODBC support])
 fi

+#
+# IPv6 and modern network address translation
+#
+AC_CHECK_FUNCS(getaddrinfo)
+AC_CHECK_TYPE([struct sockaddr_storage],
+        [AC_DEFINE(HAVE_STRUCT_SOCKADDR_STORAGE, 1, [Define to 1 if you have struct sockaddr_storage.])],
+        [],
+[$ac_includes_default
+#include <netinet/in.h>])
+
+HAVE_IPV6=no
+AC_CHECK_TYPE([struct sockaddr_in6],
+        [AC_DEFINE(HAVE_IPV6, 1, [Define to 1 if you have support for IPv6.])],
+        [HAVE_IPV6=no],
+[$ac_includes_default
+#include <netinet/in.h>])
+AC_SUBST(HAVE_IPV6)
+

 #
 # Default odbc version number (--with-odbcver), default 0x0300
diff -B -u -r --new-file psqlodbc-08.00.0102.old/socket.c psqlodbc-08.00.0102.new/socket.c
--- psqlodbc-08.00.0102.old/socket.c    2005-03-02 16:11:08.000000000 +0100
+++ psqlodbc-08.00.0102.new/socket.c    2005-08-10 15:17:53.000000000 +0200
@@ -101,19 +101,150 @@

     if (self->buffer_out)
         free(self->buffer_out);
-    if (self->sadr != (struct sockaddr *) &(self->sadr_in))
+    if (self->sadr != (struct sockaddr *) &(self->ss))
         free(self->sadr);

     free(self);
 }

+static int
+SOCK_connect_to_inet_address(SocketClass *self)
+{
+    if (!self->sadr)
+        return 0;
+
+    if ((self->socket = socket(self->sadr->sa_family, SOCK_STREAM, 0))==-1)
+    {
+        self->errornumber = SOCKET_COULD_NOT_CREATE_SOCKET;
+        self->errormsg = "Could not create Socket.";
+        return 0;
+    }
+#ifdef    TCP_NODELAY
+    else
+    {
+        int i = 1;
+        if (setsockopt(self->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i)) < 0)
+        {
+            self->errornumber = SOCKET_COULD_NOT_CONNECT;
+            self->errormsg = "Could not set socket to NODELAY.";
+            closesocket(self->socket);
+            self->socket = (SOCKETFD) - 1;
+            return 0;
+        }
+    }
+#endif
+    if (connect(self->socket, self->sadr, self->sadr_len) < 0)
+    {
+        self->errornumber = SOCKET_COULD_NOT_CONNECT;
+        self->errormsg = "Could not connect to remote socket.";
+        closesocket(self->socket);
+        self->socket = (SOCKETFD) - 1;
+        return 0;
+    }
+    return 1;
+}

-char
-SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname
 #ifdef HAVE_UNIX_SOCKETS
-        , char *uds /* unix domain socket path */
+static int
+SOCK_connect_to_UNIX(SocketClass *self, unsigned short port, char *hostname, char *uds)
+{
+    struct sockaddr_un *un;
+
+    /*
+     * In sockaddr_storage should be enough space all types of addresses, but
+     * maybe some other systems use a variable sized sockaddr_un to hold a
+     * bigger pathname... ???*     -- kzak
+     */
+    un = (struct sockaddr_un *) malloc(sizeof(struct sockaddr_un));
+    if (!un)
+    {
+        self->errornumber = SOCKET_COULD_NOT_CREATE_SOCKET;
+        self->errormsg = "coulnd't allocate memory for un.";
+        return 0;
+    }
+    un->sun_family = AF_UNIX;
+    UNIXSOCK_PATH(un, port, uds);
+    self->sadr_len = UNIXSOCK_LEN(un);
+    self->sadr = (struct sockaddr *) un;
+
+    self->socket = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (self->socket == -1)
+    {
+        self->errornumber = SOCKET_COULD_NOT_CREATE_SOCKET;
+        self->errormsg = "Could not create Socket.";
+        return 0;
+    }
+    if (connect(self->socket, self->sadr, self->sadr_len) < 0)
+    {
+        self->errornumber = SOCKET_COULD_NOT_CONNECT;
+        self->errormsg = "Could not connect to remote socket.";
+        closesocket(self->socket);
+        self->socket = (SOCKETFD) - 1;
+        return 0;
+    }
+    return 1;
+}
 #endif
-        )
+
+#ifdef HAVE_GETADDRINFO
+/*
+ * This version is based on getaddrinfo(), it means thread safe, AF_INET and AF_INET6...
+ *     -- hans-Juergen Schoenig <hs@cybertec.at> (10-Aug-2005)
+ */
+static int
+SOCK_connect_to_INET(SocketClass *self, unsigned short port, char *hostname)
+{
+    struct addrinfo hints;
+    struct addrinfo *res = NULL;
+
+    if (hostname==NULL || *hostname=='\0')
+        return 0;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = PF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+
+    if (getaddrinfo(hostname, NULL, &hints, &res)==0)
+    {
+        while (res)
+        {
+            /* initialize address pointer */
+            self->sadr = (struct sockaddr *) &self->ss;
+#ifdef HAVE_IPV6
+            if (res->ai_family==AF_INET6)
+            {
+                self->sadr_len = sizeof(struct sockaddr_in6);
+                memcpy(self->sadr, res->ai_addr, self->sadr_len);
+                ((struct sockaddr_in6 *) self->sadr)->sin6_port = htons(port);
+            }
+            else
+#endif
+            if (res->ai_family==AF_INET)
+            {
+                self->sadr_len = sizeof(struct sockaddr_in6);
+                memcpy(self->sadr, res->ai_addr, self->sadr_len);
+                ((struct sockaddr_in *) self->sadr)->sin_port = htons(port);
+            }
+            if (SOCK_connect_to_inet_address(self))
+                return 1;
+
+            /* well, let's try next possible address */
+            res = res->ai_next;
+        }
+    }
+    self->errornumber = SOCKET_HOST_NOT_FOUND;
+    self->errormsg = "Could not resolve hostname.";
+    return 0;
+}
+
+#else /* !HAVE_GETADDRINFO */
+
+/*
+ * Classic version without getaddrinfo() and without ipv6 support
+ * ... deprecated on modern OS!
+ */
+static int
+SOCK_connect_to_INET(SocketClass *self, unsigned short port, char *hostname)
 {
 #if defined (POSIX_MULTITHREAD_SUPPORT)
     const int bufsz = 8192;
@@ -125,129 +256,100 @@
     struct hostent* hp;
 #endif /* POSIX_MULTITHREAD_SUPPORT */
     struct sockaddr_in *in;
-#ifdef    HAVE_UNIX_SOCKETS
-    struct sockaddr_un *un;
-#endif /* HAVE_UNIX_SOCKETS */
     int    family, sLen;
 #ifdef WIN32
         UInt4 iaddr;
 #else
     in_addr_t iaddr;
 #endif
-
-    if (self->socket != -1)
-    {
-        self->errornumber = SOCKET_ALREADY_CONNECTED;
-        self->errormsg = "Socket is already connected";
+
+    if (hostname==NULL || *hostname=='\0')
         return 0;
-    }

-
-    /*
-     * If it is a valid IP address, use it. Otherwise use AF_UNIX socket.
-     */
-    if (hostname && hostname[0])
+    iaddr = inet_addr(hostname);
+
+    self->sadr = (struct sockaddr *) &self->ss;
+    self->sadr_len = sizeof(struct sockaddr_in);
+
+    in = (struct sockaddr_in *) &self->ss;
+    in->sin_family = family = AF_INET;
+    in->sin_port = htons(port);
+
+    if (iaddr == INADDR_NONE)
     {
-        iaddr = inet_addr(hostname);
-        memset((char *) &(self->sadr_in), 0, sizeof(self->sadr_in));
-        in = &(self->sadr_in);
-        in->sin_family = family = AF_INET;
-        in->sin_port = htons(port);
-        sLen = sizeof(self->sadr_in);
-        if (iaddr == INADDR_NONE)
-        {
 #if defined (POSIX_MULTITHREAD_SUPPORT)
-  #if defined (HAVE_GETIPNODEBYNAME) /* Free-BSD ? */
-            hp = getipnodebyname(hostname, AF_INET, 0, &error);
-  #elif defined (PGS_REENTRANT_API_1) /* solaris, irix */
-            hp = gethostbyname_r(hostname, hp, buf, bufsz, &error);
-  #elif defined (PGS_REENTRANT_API_2) /* linux */
-            int result = 0;
-            result = gethostbyname_r(hostname, hp, buf, bufsz, &hp, &error);
-            if (result)
-                hp = NULL;
-  #else
-            hp = gethostbyname(hostname);
-  #endif /* HAVE_GETIPNODEBYNAME */
+#if defined (HAVE_GETIPNODEBYNAME) /* Free-BSD ? */
+        hp = getipnodebyname(hostname, AF_INET, 0, &error);
+#elif defined (PGS_REENTRANT_API_1) /* solaris, irix */
+        hp = gethostbyname_r(hostname, hp, buf, bufsz, &error);
+#elif defined (PGS_REENTRANT_API_2) /* linux */
+        int result = 0;
+        result = gethostbyname_r(hostname, hp, buf, bufsz, &hp, &error);
+        if (result)
+            hp = NULL;
 #else
-            hp = gethostbyname(hostname);
-#endif /* POSIX_MULTITHREAD_SUPPORT */
-            if (hp == NULL)
-            {
-                self->errornumber = SOCKET_HOST_NOT_FOUND;
-                self->errormsg = "Could not resolve hostname.";
-                return 0;
-            }
-            memcpy(&(in->sin_addr), hp->h_addr, hp->h_length);
-        }
-        else
-            memcpy(&(in->sin_addr), (struct in_addr *) & iaddr, sizeof(iaddr));
-        self->sadr = (struct sockaddr *) in;
-
-#if defined (HAVE_GETIPNODEBYNAME)
-        freehostent(hp);
+        hp = gethostbyname(hostname);
 #endif /* HAVE_GETIPNODEBYNAME */
-    }
-    else
-#ifdef    HAVE_UNIX_SOCKETS
-    {
-        un = (struct sockaddr_un *) malloc(sizeof(struct sockaddr_un));
-        if (!un)
+#else
+        hp = gethostbyname(hostname);
+#endif /* POSIX_MULTITHREAD_SUPPORT */
+        if (hp == NULL)
         {
-            self->errornumber = SOCKET_COULD_NOT_CREATE_SOCKET;
-            self->errormsg = "coulnd't allocate memory for un.";
+            self->errornumber = SOCKET_HOST_NOT_FOUND;
+            self->errormsg = "Could not resolve hostname.";
             return 0;
         }
-        un->sun_family = family = AF_UNIX;
-        /* passing NULL means that this only suports the pg default "/tmp" */
-        UNIXSOCK_PATH(un, port, uds);
-        sLen = UNIXSOCK_LEN(un);
-        self->sadr = (struct sockaddr *) un;
-    }
-#else
-    {
-        self->errornumber = SOCKET_HOST_NOT_FOUND;
-        self->errormsg = "Hostname isn't specified.";
-        return 0;
+        memcpy(&(in->sin_addr), hp->h_addr, hp->h_length);
     }
-#endif /* HAVE_UNIX_SOCKETS */
+    else
+        memcpy(&(in->sin_addr), (struct in_addr *) & iaddr, sizeof(iaddr));

-    self->socket = socket(family, SOCK_STREAM, 0);
-    if (self->socket == -1)
+#if defined (HAVE_GETIPNODEBYNAME)
+    freehostent(hp);
+#endif /* HAVE_GETIPNODEBYNAME */
+
+    return SOCK_connect_to_inet_address(self);
+}
+
+#endif /* HAVE_GETADDRINFO */
+
+/*
+ * Main of AF_UNIX, AF_INET and AF_INET6 connection
+ */
+char
+SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname
+#ifdef HAVE_UNIX_SOCKETS
+        , char *uds /* unix domain socket path */
+#endif
+        )
+{
+    if (self->socket != -1)
     {
-        self->errornumber = SOCKET_COULD_NOT_CREATE_SOCKET;
-        self->errormsg = "Could not create Socket.";
+        self->errornumber = SOCKET_ALREADY_CONNECTED;
+        self->errormsg = "Socket is already connected";
         return 0;
     }
-#ifdef    TCP_NODELAY
-    if (family == AF_INET)
-    {
-        int i, len;

-        i = 1;
-        len = sizeof(i);
-        if (setsockopt(self->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &i, len) < 0)
-        {
-            self->errornumber = SOCKET_COULD_NOT_CONNECT;
-            self->errormsg = "Could not set socket to NODELAY.";
-            closesocket(self->socket);
-            self->socket = (SOCKETFD) - 1;
-            return 0;
-        }
-    }
-#endif /* TCP_NODELAY */
+    /* zeroize sockaddr storage */
+    memset((char *) &self->ss, 0, sizeof(self->ss));

-    self->sadr_len = sLen;
-    if (connect(self->socket, self->sadr, sLen) < 0)
+    /*
+        * AF_UNIX or nothing...
+     */
+    if (hostname==NULL || *hostname=='\0')
+#ifdef  HAVE_UNIX_SOCKETS
+        return SOCK_connect_to_UNIX(self, port, hostname, uds);
+#else
     {
-        self->errornumber = SOCKET_COULD_NOT_CONNECT;
-        self->errormsg = "Could not connect to remote socket.";
-        closesocket(self->socket);
-        self->socket = (SOCKETFD) - 1;
+        self->errornumber = SOCKET_HOST_NOT_FOUND;
+        self->errormsg = "Hostname isn't specified.";
         return 0;
     }
-
-    return 1;
+#endif
+    /*
+     * AF_INET
+     */
+    return SOCK_connect_to_INET(self, port, hostname);
 }


diff -B -u -r --new-file psqlodbc-08.00.0102.old/socket.h psqlodbc-08.00.0102.new/socket.h
--- psqlodbc-08.00.0102.old/socket.h    2005-03-02 16:11:09.000000000 +0100
+++ psqlodbc-08.00.0102.new/socket.h    2005-08-10 15:20:28.000000000 +0200
@@ -55,6 +55,35 @@
 #define SOCKET_GET_INT_WRONG_LENGTH            9
 #define SOCKET_CLOSED                        10

+/*
+ * sockaddr storage -- see glibc or RFC2553
+ */
+#ifndef HAVE_STRUCT_SOCKADDR_STORAGE
+
+/* Desired design of maximum size and alignment */
+#define _SS_MAXSIZE    128            /* Implementation specific max size */
+#define _SS_ALIGNSIZE  (sizeof (int64_t))    /* Implementation specific desired alignment */
+
+/* Definitions used for sockaddr_storage structure paddings design. */
+#define _SS_PAD1SIZE   (_SS_ALIGNSIZE - sizeof (sa_family_t))
+#define _SS_PAD2SIZE   (_SS_MAXSIZE - (sizeof (sa_family_t)+_SS_PAD1SIZE+_SS_ALIGNSIZE))
+
+struct sockaddr_storage
+{
+    sa_family_t  __ss_family;     /* address family */
+    /* Following fields are implementation specific */
+    char      __ss_pad1[_SS_PAD1SIZE];
+              /* 6 byte pad, this is to make implementation
+               * specific pad up to alignment field that
+               * follows explicit in the data structure */
+    int64_t   __ss_align;     /* field to force desired structure */
+               /* storage alignment */
+    char      __ss_pad2[_SS_PAD2SIZE];
+              /* 112 byte pad to achieve desired size, */
+              /* _SS_MAXSIZE value minus size of ss_family */
+              /* __ss_pad1, __ss_align fields is 112 */
+};
+#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */

 struct SocketClass_
 {
@@ -68,11 +97,12 @@

     SOCKETFD    socket;

-    char       *errormsg;
-    int            errornumber;
-    struct sockaddr    *sadr; /* Used for handling connections for cancel */
-    int        sadr_len;
-    struct sockaddr_in sadr_in; /* Used for INET connections */
+    char           *errormsg;
+    int        errornumber;
+
+    struct sockaddr_storage    ss;        /* generic address IPV4, IPV6, UNIX... */
+    struct sockaddr        *sadr;        /* pointer to .ss or new allocated space for AF_UNIX */
+    int            sadr_len;

     char        reverse;        /* used to handle Postgres 6.2 protocol
                                  * (reverse byte order) */
diff -B -u -r --new-file psqlodbc-08.00.0102.old/tests/Makefile psqlodbc-08.00.0102.new/tests/Makefile
--- psqlodbc-08.00.0102.old/tests/Makefile    1970-01-01 01:00:00.000000000 +0100
+++ psqlodbc-08.00.0102.new/tests/Makefile    2005-08-09 15:37:33.000000000 +0200
@@ -0,0 +1,23 @@
+
+PROGS = psqlodbc_connect
+
+override CFLAGS += `odbc_config --cflags`
+override LDFLAGS += `odbc_config --libs`
+
+
+all: $(PROGS)
+
+psqlodbc_connect: psqlodbc_connect.c
+    $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+
+dep depend:
+    $(CC) -MM $(CPPFLAGS) *.c >depend
+
+clean:
+    rm -f $(PROGS)
+
+ifeq (depend,$(wildcard depend))
+include depend
+endif
+
+
diff -B -u -r --new-file psqlodbc-08.00.0102.old/tests/psqlodbc_connect.c
psqlodbc-08.00.0102.new/tests/psqlodbc_connect.c
--- psqlodbc-08.00.0102.old/tests/psqlodbc_connect.c    1970-01-01 01:00:00.000000000 +0100
+++ psqlodbc-08.00.0102.new/tests/psqlodbc_connect.c    2005-08-09 15:42:19.000000000 +0200
@@ -0,0 +1,100 @@
+/*
+ * =====================================================================================
+ *
+ *        Filename:  psqlodbc_test1.c
+ *
+ *     Description:  PostgreSQL ODBC tests: connection to server
+ *
+ *         Version:  $Id$
+ *         Created:  09.08.2005 12:57:51 CEST
+ *
+ *          Author:  Hans-Juergen Schoenig <hs@cybertec.at>
+ *
+ * =====================================================================================
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sql.h>
+#include <sqlext.h>
+
+static void
+extract_error(char *fn, SQLHANDLE handle, SQLSMALLINT type)
+{
+    SQLINTEGER i = 0;
+    SQLINTEGER native;
+    SQLCHAR state[ 7 ];
+    SQLCHAR text[256];
+    SQLSMALLINT len;
+    SQLRETURN ret;
+
+    fprintf(stderr,"The driver reported the following diagnostics: %s:\n", fn);
+    do
+    {
+        ret = SQLGetDiagRec(type, handle, ++i, state, &native,
+                    text, sizeof(text), &len );
+        if (SQL_SUCCEEDED(ret))
+            printf("\t%s:%ld:%ld:%s\n", state, i, native, text);
+    } while( ret == SQL_SUCCESS );
+}
+
+int
+main(int argc, char **argv)
+{
+    SQLCHAR *constr = NULL;
+    SQLHENV env;
+    SQLHDBC dbc;
+    SQLHSTMT stmt;
+    SQLRETURN ret;
+    SQLCHAR outstr[BUFSIZ];
+    SQLSMALLINT outstrlen;
+    SQLCHAR dbms_name[500], dbms_ver[500];
+
+    if (argc > 1 && (strcmp(argv[1], "--help")==0 || strcmp(argv[1], "-h")==0))
+    {
+        fprintf(stderr, "usage: %s [<connection-sting>]\n"
+                "       default connection string: 'DSN=PostgreSQL;'\n\n",
+                argv[0]);
+        exit(EXIT_FAILURE);
+    }
+    constr = (SQLCHAR *) (argc < 2 ? "DSN=PostgreSQL;" : argv[1]);
+
+    SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
+    SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
+
+    SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
+
+    ret = SQLDriverConnect(dbc, (void *)1, constr, SQL_NTS, outstr,
+                sizeof(outstr), &outstrlen, SQL_DRIVER_COMPLETE);
+
+    printf("Connect string: %s\n", outstr);
+
+    if (!SQL_SUCCEEDED(ret))
+    {
+        fprintf(stderr, "Failed to connect\n");
+        extract_error("SQLDriverConnect", dbc, SQL_HANDLE_DBC);
+
+        SQLFreeHandle(SQL_HANDLE_DBC, dbc);
+        SQLFreeHandle(SQL_HANDLE_ENV, env);
+
+        exit(EXIT_FAILURE);
+    }
+
+    printf("Connected\n");
+    if (ret == SQL_SUCCESS_WITH_INFO)
+        extract_error("SQLDriverConnect", dbc, SQL_HANDLE_DBC);
+
+    SQLGetInfo(dbc, SQL_DBMS_NAME, (SQLPOINTER)dbms_name, sizeof(dbms_name), NULL);
+    SQLGetInfo(dbc, SQL_DBMS_VER, (SQLPOINTER)dbms_ver, sizeof(dbms_ver), NULL);
+
+    printf("DBMS Name: %s\n", dbms_name);
+    printf("DBMS Version: %s\n", dbms_ver);
+
+    SQLDisconnect(dbc);
+    SQLFreeHandle(SQL_HANDLE_DBC, dbc);
+    SQLFreeHandle(SQL_HANDLE_ENV, env);
+
+    exit(EXIT_SUCCESS);
+}
+

pgsql-odbc by date:

Previous
From: Hans-Jürgen Schönig
Date:
Subject: troubles with configure script on solaris ...
Next
From: Bobby Brewster
Date:
Subject: HELP: Unrecognized key passed to SQLGetInfo30(#209)