I've attached a patch for libpgtcl which adds access to backend version numbers. This is via a new command: pg_version <db channel> <major varname> ?<minor varname>? ?<patch varname>? Using readonly variables rather than a command was my first choice but I decided that it was inappropiate for the library to start assigning global variable(s) when that's really the applications job and the command interface is consistent with the rest of the interface. Obviously, backend version numbers are specific to a particular connection. So I've created a new data structure, to keep the information as a distinct unit, and added an instance of the new structure to the Pg_ConnectionId type. The version information is retrieved from the given connection on first use of pg_version and cached in the new data structure for subsequent accesses. In addition to filling the named variables in the callers scope with version numbers/strings the command returns the complete string as returned by version(). It's not possible to turn this return off at the moment but I don't see it as a problem since normal methods of stopping unwanted values returned from procedures can be applied in the application if required. Perhaps the most significant change is that I've increased the package's version number from 1.3 to 1.4. This will adversly effect anyone using an application that requires a specific version of the package where their postgres installation is updated but their application has not been. I can't imagine there are many applications out there using the package management features of TCL though. I envisage this patch applied to 7.3 tip and to 7.2 for the 7.2.2 release mentioned a couple of days ago. The only problem with doing this for 7.2 that I can see is where people doing the 'package -exact require Pgtcl 1.x' thing, and how many of those are there? Even PgAccess doesn't use that. Note for commiter et al,, this patch also includes one change made in 7.3devel and not 7.2.1. That is where a test of the return value from a Tcl_SetVar call has been corrected from a test against TCL_OK to NULL. This is correct and should be applied to the 7.2 branch in my view, however, I do not know if this has already been applied there so something to watch out for. -- Nigel J. Andrews Director --- Logictree Systems Limited Computer Consultants
Index: src/interfaces/libpgtcl/pgtcl.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpgtcl/pgtcl.c,v retrieving revision 1.24 diff -c -r1.24 pgtcl.c *** src/interfaces/libpgtcl/pgtcl.c 2001/10/25 05:50:12 1.24 --- src/interfaces/libpgtcl/pgtcl.c 2002/05/16 22:07:12 *************** *** 152,158 **** Pg_listen, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); ! Tcl_PkgProvide(interp, "Pgtcl", "1.3"); return TCL_OK; } --- 152,163 ---- Pg_listen, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); ! Tcl_CreateCommand(interp, ! "pg_version", ! Pg_version, ! (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); ! ! Tcl_PkgProvide(interp, "Pgtcl", "1.4"); return TCL_OK; } Index: src/interfaces/libpgtcl/pgtclCmds.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpgtcl/pgtclCmds.c,v retrieving revision 1.61 diff -c -r1.61 pgtclCmds.c *** src/interfaces/libpgtcl/pgtclCmds.c 2002/03/04 02:41:49 1.61 --- src/interfaces/libpgtcl/pgtclCmds.c 2002/05/16 22:07:13 *************** *** 423,428 **** --- 423,431 ---- Tcl_UnregisterChannel(interp, connid->notifier_channel); #endif + if (connid->version.string) + ckfree(connid->version.string); + return Tcl_UnregisterChannel(interp, conn_chan); } *************** *** 1967,1971 **** --- 1970,2106 ---- } ckfree(caserelname); + return TCL_OK; + } + + /********************************** + Pg_version + get backend version numbers + + syntax: + pg_version connection majorvar ?minorvar? ?patchvar? + + the return result is either an error message or the full version string + returned from the backend with filled in major, minor and patch version + variables + **********************************/ + + int + Pg_version(ClientData cData, Tcl_Interp *interp, int argc, char *argv[]) + { + Pg_ConnectionId *connid; + PGconn *conn; + struct Pg_VersionNumber_s *version; + + if (argc < 3 || argc > 5) + { + Tcl_AppendResult(interp, "Wrong # of arguments\n", + "pg_version connection majorvar +?minorvar? ?patchvar?", 0); + return TCL_ERROR; + } + + conn = PgGetConnectionId(interp, argv[1], &connid); + if (conn == (PGconn *) NULL) + return TCL_ERROR; + + version = &connid->version; + + if (!version->string) + { + PGresult *result; + + result = PQexec(conn, "SELECT version()"); + + /* Transfer any notify events from libpq to Tcl event queue. */ + PgNotifyTransferEvents(connid); + + if (result && PQntuples(result)) + { + char *vers = PQgetvalue(result, 0, 0); + char *tok; + + version->string = ckalloc(strlen(vers)*2+3); + strcpy(version->string, vers); + strcpy(version->string + strlen(vers) + 1, vers); + + tok = strtok(version->string + strlen(vers) + 1, " "); + while (tok) + { + char *ecnv; + int v = strtol(tok, &ecnv, 10); + + if (ecnv != tok) + { + version->major = v; + version->major_str = tok; + *ecnv = '\0'; + + tok = ecnv + 1; + v = strtol(tok, &ecnv, 10); + if (ecnv != tok) + { + version->minor = v; + version->minor_str = tok; + if (*ecnv) + { + char *necnv = ecnv; + + if (!isspace(*necnv)) + necnv++; + + /* these tests are going to +skip making a + patch value if the has only +a single + none numeric character +after the minor + version, but I don't think +that's important */ + if (*necnv && +!isspace(*necnv)) + { + tok = necnv; + v = strtol(tok, +&necnv, 10); + version->patch_str = +tok; + if (necnv != tok) + version->patch += v; + else + if (tok != +ecnv) + +memmove(tok, ecnv, strlen(ecnv)+1); + } + + *ecnv = '\0'; + } + } + + break; + } + else + tok = strtok((char *)NULL, " "); + } + } + else + { + /* error occurred during the query */ + Tcl_SetResult(interp, PQerrorMessage(conn), TCL_VOLATILE); + return TCL_ERROR; + } + } + + if (!Tcl_SetVar(interp, + argv[2], + version->major_str ? version->major_str : "", + TCL_LEAVE_ERR_MSG) + || (argc > 3 + && !Tcl_SetVar(interp, + argv[3], + version->minor_str ? +version->minor_str : "", + TCL_LEAVE_ERR_MSG)) + || (argc > 4 + && !Tcl_SetVar(interp, + argv[4], + version->patch_str ? +version->patch_str : "", + TCL_LEAVE_ERR_MSG))) + { + return TCL_ERROR; + } + + Tcl_SetResult(interp, version->string, TCL_VOLATILE); + return TCL_OK; } Index: src/interfaces/libpgtcl/pgtclCmds.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpgtcl/pgtclCmds.h,v retrieving revision 1.25 diff -c -r1.25 pgtclCmds.h *** src/interfaces/libpgtcl/pgtclCmds.h 2001/11/05 17:46:37 1.25 --- src/interfaces/libpgtcl/pgtclCmds.h 2002/05/16 22:07:14 *************** *** 50,55 **** --- 50,66 ---- Tcl_HashTable notify_hash; /* Active pg_listen requests */ } Pg_TclNotifies; + struct Pg_VersionNumber_s + { + char *string; + char *major_str; + int major; + char *minor_str; + int minor; + char *patch_str; + int patch; + }; + typedef struct Pg_ConnectionId_s { char id[32]; *************** *** 70,75 **** --- 81,87 ---- #else int notifier_socket; /* PQsocket on which notifier is listening */ #endif + struct Pg_VersionNumber_s version; /* backend version numbers */ } Pg_ConnectionId; /* Values of res_copyStatus */ *************** *** 127,132 **** --- 139,147 ---- extern int Pg_lo_export( ClientData cData, Tcl_Interp *interp, int argc, char *argv[]); extern int Pg_listen( + ClientData cData, Tcl_Interp *interp, int argc, char *argv[]); + + extern int Pg_version( ClientData cData, Tcl_Interp *interp, int argc, char *argv[]); #endif /* PGTCLCMDS_H */ Index: src/interfaces/libpgtcl/pgtclId.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/libpgtcl/pgtclId.c,v retrieving revision 1.29 diff -c -r1.29 pgtclId.c *** src/interfaces/libpgtcl/pgtclId.c 2001/11/05 17:46:37 1.29 --- src/interfaces/libpgtcl/pgtclId.c 2002/05/16 22:07:14 *************** *** 188,193 **** --- 188,198 ---- connid->results[i] = NULL; connid->notify_list = NULL; connid->notifier_running = 0; + connid->version.string = (char *)NULL; + connid->version.major_str = (char *)NULL; + connid->version.minor_str = (char *)NULL; + connid->version.patch_str = (char *)NULL; + connid->version.major = connid->version.minor = connid->version.patch = 0; sprintf(connid->id, "pgsql%d", PQsocket(conn));
---------------------------(end of broadcast)--------------------------- TIP 5: Have you checked our extensive FAQ? http://www.postgresql.org/users-lounge/docs/faq.html