Re: libpq++

From: Justin Banks <justinb(at)mr-boo(dot)com>
To: Bruce Momjian <pgman(at)candle(dot)pha(dot)pa(dot)us>
Cc: justinb(at)tricord(dot)com, PostgreSQL-interfaces <pgsql-interfaces(at)postgresql(dot)org>
Subject: Re: libpq++
Date: 2001-04-30 03:07:10
Message-ID: 15084.54539.44529.709222@dervish.mr-boo.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-interfaces

>>>>> "Bruce" == Bruce Momjian <pgman(at)candle(dot)pha(dot)pa(dot)us> writes:

Bruce> I am not so much the libpq++ maintainer as the one who applies
Bruce> people's patches. There is an interfaces list that would love to
Bruce> hear about your work. I am CC'ing them on this. We certainly need
Bruce> libpq++ improvements. If you would prefer not to deal with the
Bruce> interfaces list, send something to me and I will send it over there.

Here's what I've done. The following code will create a number of different
connections to the B.E., even though there's no reason to have more than
one. This prevents one from inheriting PgDatabase, and then creating an array
of 100 or so of those objects.

#include <iostream.h>
#include <stdlib.h>
#include <libpq++.h>

class base : public PgDatabase {
public:
base() {
cout << "base class instantiating" << endl;
Connect("host=db dbname=golem user=golemadmin password=bleen");
}
virtual ~base() {
cout << "Base class destructing" << endl;
}

};

class derived : public base {
public:
derived() {
cout << "derived class instantiating" << endl;
}
~derived() {
cout << "derived class destructing" << endl;
}
};

int
main(int argc, char* argv[]) {
int cnt, tuples;
derived* classes, *ptr;
derived single;

if(argc > 1)
cnt = atoi(argv[1]);
else
cnt = 5;

cout << "getting array of " << cnt << " objects" << endl;
classes = new derived[cnt];

cout << "array allocated, sleeping for validation" << endl;
sleep(5);
for(int i = 0; i < cnt; i++) {
cout << "testing object " << i << endl;
ptr = &(classes[i]);
ptr->Exec("select * from defaults");
tuples = ptr->Tuples();
cout << "got " << tuples << " tuples" << endl;
for(int j = 0; j < tuples; j++) {
cout << "4th field is " << ptr->GetValue(j, 4) << endl;
}
}
delete[] classes;

cout << "aggregates deleted" << endl;
single.Exec("select table_name from tables limit 1");
cout << "exec of single finished" << endl;
cout << "value is " << single.GetValue(0, 0) << endl;
exit(0);
}

What I'd like to have happen is for a total of one connection to the database
to be made, and have all these objects share that connection transparently. I
made this happen by using a shared memory segment to store the connection
pointer. Here's the diff. It doesn't seem to break anything, but then again,
my coverage is rather limited at this point.

Diff follows :

--- pgconnection.cc.orig Sun Apr 29 16:09:02 2001
+++ pgconnection.cc Sun Apr 29 21:35:36 2001
@@ -17,7 +17,6 @@

#include "pgconnection.h"

-
// ****************************************************************
//
// PgConnection Implementation
@@ -54,7 +53,15 @@
// close down the connection if there is one
void PgConnection::CloseConnection()
{
- // if the connection is open, close it first
+#if USING_SYSV_IPC == 1
+ struct shmid_ds shminfo;
+
+ shmctl(shmid, IPC_STAT, &shminfo);
+ //
+ // if we're the only one left ...
+ //
+ if(shminfo.shm_nattch == 1) {
+#endif
if ( pgCloseConnection ) {
if(pgResult) PQclear(pgResult);
pgResult=NULL;
@@ -62,6 +69,10 @@
pgConn=NULL;
pgCloseConnection=0;
}
+#if USING_SYSV_IPC == 1
+ }
+ shmdt(addr);
+#endif
}


@@ -69,11 +80,60 @@
// establish a connection to a backend
ConnStatusType PgConnection::Connect(const char* conninfo)
{
+#if USING_SYSV_IPC == 1
+ int key = (int)getpid();
+ struct shmid_ds shminfo;
+#endif
+
// if the connection is open, close it first
CloseConnection();

// Connect to the database
+#if USING_SYSV_IPC == 1
+ if((shmid = shmget(key, sizeof(addr), IPC_CREAT|SHM_W|SHM_R)) >= 0) {
+ if((addr = (int*)shmat(shmid, 0, 0)) > 0) {
+ //
+ // now, we have our segment. If we're the first attacher, then we need
+ // to do the connect. If we're not the first attacher, then the
+ // connection is already made, we just need to check and make sure
+ // it's okay, connecting again if we have to.
+ //
+ if(shmctl(shmid, IPC_STAT, &shminfo) == 0) {
+ if(shminfo.shm_nattch == 1) {
+ pgConn = PQconnectdb(conninfo);
+ *addr = (int)pgConn;
+ }
+ else {
+ pgConn = (PGconn*)*addr;
+ if(Status() == CONNECTION_BAD) {
+ pgConn = PQconnectdb(conninfo);
+ *addr = (int)pgConn;
+ }
+ }
+ }
+ else {
+ //
+ // fallback to safe connection
+ //
+ pgConn = PQconnectdb(conninfo);
+ }
+ }
+ else {
+ //
+ // fallback to safe connection
+ //
+ pgConn = PQconnectdb(conninfo);
+ }
+ }
+ else {
+ //
+ // fallback to safe connection
+ //
+ pgConn = PQconnectdb(conninfo);
+ }
+#else
pgConn = PQconnectdb(conninfo);
+#endif

// Now we have a connection we must close (even if it's bad!)
pgCloseConnection = 1;
--- pgconnection.h.orig Sun Apr 29 16:09:54 2001
+++ pgconnection.h Sun Apr 29 17:07:57 2001
@@ -46,6 +46,15 @@
using namespace std;
#endif

+#if HAVE_SYS_SHM_H == 1 && HAVE_SYS_IPC_H == 1
+ #include <sys/types.h>
+ #include <sys/ipc.h>
+ #include <sys/shm.h>
+ #include <unistd.h>
+ #define USING_SYSV_IPC 1
+#else
+ #define USING_SYSV_IPC 0
+#endif

// ****************************************************************
//
@@ -91,6 +100,9 @@
// so make copy constructor and assignment op private.
PgConnection(const PgConnection&);
PgConnection& operator= (const PgConnection&);
+#if USING_SYSV_IPC == 1
+ int shmid, *addr;
+#endif
};

#endif // PGCONNECTION_H

I think that's it. I've got more at work, so I may have missed something, but
I don't think so.

-justinb

--
Justin Banks @ home
Distrust any enterprise that requires new clothes. -Thoreau

In response to

  • Re: libpq++ at 2001-04-29 20:04:49 from Bruce Momjian

Browse pgsql-interfaces by date

  From Date Subject
Next Message Tom Lane 2001-04-30 05:52:57 Re: Re: can external C-function get multiple rows?
Previous Message Bruce Momjian 2001-04-29 20:04:49 Re: libpq++