Change PQconndefaults() to return a malloc'd array, instead of a static

array.  This allows processing of conninfo strings to be made thread-safe,
at the cost of a small memory leak in applications that use
PQconndefaults() and are not updated to free the returned array via
the new PQconninfoFree() function.  But PQconndefaults() is probably not
used very much, so this seems like a good compromise.
This commit is contained in:
Tom Lane 2000-03-11 03:08:37 +00:00
parent 773e84f52a
commit a71daab4b4
8 changed files with 234 additions and 137 deletions

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclCmds.c,v 1.47 2000/02/27 07:44:22 tgl Exp $ * $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclCmds.c,v 1.48 2000/03/11 03:08:35 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -237,25 +237,32 @@ tcl_value(char *value)
int int
Pg_conndefaults(ClientData cData, Tcl_Interp *interp, int argc, char **argv) Pg_conndefaults(ClientData cData, Tcl_Interp *interp, int argc, char **argv)
{ {
PQconninfoOption *options = PQconndefaults();
PQconninfoOption *option; PQconninfoOption *option;
Tcl_DString result; Tcl_DString result;
char ibuf[32]; char ibuf[32];
Tcl_DStringInit(&result); if (options)
for (option = PQconndefaults(); option->keyword != NULL; option++)
{ {
char *val = option->val ? option->val : ""; Tcl_DStringInit(&result);
sprintf(ibuf, "%d", option->dispsize); for (option = options; option->keyword != NULL; option++)
Tcl_DStringStartSublist(&result); {
Tcl_DStringAppendElement(&result, option->keyword); char *val = option->val ? option->val : "";
Tcl_DStringAppendElement(&result, option->label);
Tcl_DStringAppendElement(&result, option->dispchar); sprintf(ibuf, "%d", option->dispsize);
Tcl_DStringAppendElement(&result, ibuf); Tcl_DStringStartSublist(&result);
Tcl_DStringAppendElement(&result, val); Tcl_DStringAppendElement(&result, option->keyword);
Tcl_DStringEndSublist(&result); Tcl_DStringAppendElement(&result, option->label);
Tcl_DStringAppendElement(&result, option->dispchar);
Tcl_DStringAppendElement(&result, ibuf);
Tcl_DStringAppendElement(&result, val);
Tcl_DStringEndSublist(&result);
}
Tcl_DStringResult(interp, &result);
PQconninfoFree(options);
} }
Tcl_DStringResult(interp, &result);
return TCL_OK; return TCL_OK;
} }

View File

@ -10,7 +10,7 @@
* exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes). * exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.37 2000/02/07 23:10:08 petere Exp $ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.38 2000/03/11 03:08:36 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -75,7 +75,7 @@ struct authsvc
* allowed. Unauthenticated connections are disallowed unless there * allowed. Unauthenticated connections are disallowed unless there
* isn't any authentication system. * isn't any authentication system.
*/ */
static struct authsvc authsvcs[] = { static const struct authsvc authsvcs[] = {
#ifdef KRB4 #ifdef KRB4
{"krb4", STARTUP_KRB4_MSG, 1}, {"krb4", STARTUP_KRB4_MSG, 1},
{"kerberos", STARTUP_KRB4_MSG, 1}, {"kerberos", STARTUP_KRB4_MSG, 1},
@ -94,7 +94,7 @@ static struct authsvc authsvcs[] = {
{"password", STARTUP_PASSWORD_MSG, 0} {"password", STARTUP_PASSWORD_MSG, 0}
}; };
static int n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc); static const int n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
#ifdef KRB4 #ifdef KRB4
/*---------------------------------------------------------------- /*----------------------------------------------------------------
@ -549,7 +549,14 @@ fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
* *
* Set/return the authentication service currently selected for use by the * Set/return the authentication service currently selected for use by the
* frontend. (You can only use one in the frontend, obviously.) * frontend. (You can only use one in the frontend, obviously.)
*
* NB: This is not thread-safe if different threads try to select different
* authentication services! It's OK for fe_getauthsvc to select the default,
* since that will be the same for all threads, but direct application use
* of fe_setauthsvc is not thread-safe. However, use of fe_setauthsvc is
* deprecated anyway...
*/ */
static int pg_authsvc = -1; static int pg_authsvc = -1;
void void
@ -558,7 +565,7 @@ fe_setauthsvc(const char *name, char *PQerrormsg)
int i; int i;
for (i = 0; i < n_authsvcs; ++i) for (i = 0; i < n_authsvcs; ++i)
if (!strcmp(name, authsvcs[i].name)) if (strcmp(name, authsvcs[i].name) == 0)
{ {
pg_authsvc = i; pg_authsvc = i;
break; break;

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.122 2000/02/24 15:53:12 momjian Exp $ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.123 2000/03/11 03:08:36 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -76,35 +76,25 @@ struct pg_setenv_state
} state; } state;
PGconn *conn; PGconn *conn;
PGresult *res; PGresult *res;
struct EnvironmentOptions *eo; const struct EnvironmentOptions *eo;
} ; } ;
static int connectDBStart(PGconn *conn);
static int connectDBComplete(PGconn *conn);
#ifdef USE_SSL #ifdef USE_SSL
static SSL_CTX *SSL_context = NULL; static SSL_CTX *SSL_context = NULL;
#endif #endif
static PGconn *makeEmptyPGconn(void);
static void freePGconn(PGconn *conn);
static void closePGconn(PGconn *conn);
static int conninfo_parse(const char *conninfo, PQExpBuffer errorMessage);
static char *conninfo_getval(char *keyword);
static void conninfo_free(void);
static void defaultNoticeProcessor(void *arg, const char *message);
#define NOTIFYLIST_INITIAL_SIZE 10 #define NOTIFYLIST_INITIAL_SIZE 10
#define NOTIFYLIST_GROWBY 10 #define NOTIFYLIST_GROWBY 10
/* ---------- /* ----------
* Definition of the conninfo parameters and their fallback resources. * Definition of the conninfo parameters and their fallback resources.
*
* If Environment-Var and Compiled-in are specified as NULL, no * If Environment-Var and Compiled-in are specified as NULL, no
* fallback is available. If after all no value can be determined * fallback is available. If after all no value can be determined
* for an option, an error is returned. * for an option, an error is returned.
* *
* The values for dbname and user are treated special in conninfo_parse. * The values for dbname and user are treated specially in conninfo_parse.
* If the Compiled-in resource is specified as a NULL value, the * If the Compiled-in resource is specified as a NULL value, the
* user is determined by fe_getauthname() and for dbname the user * user is determined by fe_getauthname() and for dbname the user
* name is copied. * name is copied.
@ -112,23 +102,30 @@ static void defaultNoticeProcessor(void *arg, const char *message);
* The Label and Disp-Char entries are provided for applications that * The Label and Disp-Char entries are provided for applications that
* want to use PQconndefaults() to create a generic database connection * want to use PQconndefaults() to create a generic database connection
* dialog. Disp-Char is defined as follows: * dialog. Disp-Char is defined as follows:
* "" Normal input field * "" Normal input field
* "*" Password field - hide value
* "D" Debug option - don't show by default
*
* PQconninfoOptions[] is a constant static array that we use to initialize
* a dynamically allocated working copy. All the "val" fields in
* PQconninfoOptions[] *must* be NULL. In a working copy, non-null "val"
* fields point to malloc'd strings that should be freed when the working
* array is freed (see PQconninfoFree).
* ---------- * ----------
*/ */
static PQconninfoOption PQconninfoOptions[] = { static const PQconninfoOption PQconninfoOptions[] = {
/* ----------------------------------------------------------------- */ /* "authtype" is no longer used, so mark it "don't show". We keep it
/* Option-name Environment-Var Compiled-in Current value */ * in the array so as not to reject conninfo strings from old apps that
/* Label Disp-Char */ * might still try to set it.
/* ----------------- --------------- --------------- --------------- */ */
/* "authtype" is ignored as it is no longer used. */
{"authtype", "PGAUTHTYPE", DefaultAuthtype, NULL, {"authtype", "PGAUTHTYPE", DefaultAuthtype, NULL,
"Database-Authtype", "", 20}, "Database-Authtype", "D", 20},
{"user", "PGUSER", NULL, NULL, {"user", "PGUSER", NULL, NULL,
"Database-User", "", 20}, "Database-User", "", 20},
{"password", "PGPASSWORD", DefaultPassword, NULL, {"password", "PGPASSWORD", DefaultPassword, NULL,
"Database-Password", "", 20}, "Database-Password", "*", 20},
{"dbname", "PGDATABASE", NULL, NULL, {"dbname", "PGDATABASE", NULL, NULL,
"Database-Name", "", 20}, "Database-Name", "", 20},
@ -147,12 +144,13 @@ static PQconninfoOption PQconninfoOptions[] = {
{"options", "PGOPTIONS", DefaultOption, NULL, {"options", "PGOPTIONS", DefaultOption, NULL,
"Backend-Debug-Options", "D", 40}, "Backend-Debug-Options", "D", 40},
/* ----------------- --------------- --------------- --------------- */
/* Terminating entry --- MUST BE LAST */
{NULL, NULL, NULL, NULL, {NULL, NULL, NULL, NULL,
NULL, NULL, 0} NULL, NULL, 0}
}; };
static struct EnvironmentOptions static const struct EnvironmentOptions
{ {
const char *envName, const char *envName,
*pgName; *pgName;
@ -181,6 +179,18 @@ static struct EnvironmentOptions
}; };
static int connectDBStart(PGconn *conn);
static int connectDBComplete(PGconn *conn);
static PGconn *makeEmptyPGconn(void);
static void freePGconn(PGconn *conn);
static void closePGconn(PGconn *conn);
static PQconninfoOption *conninfo_parse(const char *conninfo,
PQExpBuffer errorMessage);
static char *conninfo_getval(PQconninfoOption *connOptions,
const char *keyword);
static void defaultNoticeProcessor(void *arg, const char *message);
/* ---------------- /* ----------------
* Connecting to a Database * Connecting to a Database
* *
@ -262,6 +272,7 @@ PGconn *
PQconnectStart(const char *conninfo) PQconnectStart(const char *conninfo)
{ {
PGconn *conn; PGconn *conn;
PQconninfoOption *connOptions;
char *tmp; char *tmp;
/* ---------- /* ----------
@ -274,37 +285,42 @@ PQconnectStart(const char *conninfo)
return (PGconn *) NULL; return (PGconn *) NULL;
/* ---------- /* ----------
* Parse the conninfo string and save settings in conn structure * Parse the conninfo string
* ---------- * ----------
*/ */
if (conninfo_parse(conninfo, &conn->errorMessage) < 0) connOptions = conninfo_parse(conninfo, &conn->errorMessage);
if (connOptions == NULL)
{ {
conn->status = CONNECTION_BAD; conn->status = CONNECTION_BAD;
conninfo_free(); /* errorMessage is already set */
return conn; return conn;
} }
tmp = conninfo_getval("hostaddr");
/*
* Move option values into conn structure
*/
tmp = conninfo_getval(connOptions, "hostaddr");
conn->pghostaddr = tmp ? strdup(tmp) : NULL; conn->pghostaddr = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval("host"); tmp = conninfo_getval(connOptions, "host");
conn->pghost = tmp ? strdup(tmp) : NULL; conn->pghost = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval("port"); tmp = conninfo_getval(connOptions, "port");
conn->pgport = tmp ? strdup(tmp) : NULL; conn->pgport = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval("tty"); tmp = conninfo_getval(connOptions, "tty");
conn->pgtty = tmp ? strdup(tmp) : NULL; conn->pgtty = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval("options"); tmp = conninfo_getval(connOptions, "options");
conn->pgoptions = tmp ? strdup(tmp) : NULL; conn->pgoptions = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval("dbname"); tmp = conninfo_getval(connOptions, "dbname");
conn->dbName = tmp ? strdup(tmp) : NULL; conn->dbName = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval("user"); tmp = conninfo_getval(connOptions, "user");
conn->pguser = tmp ? strdup(tmp) : NULL; conn->pguser = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval("password"); tmp = conninfo_getval(connOptions, "password");
conn->pgpass = tmp ? strdup(tmp) : NULL; conn->pgpass = tmp ? strdup(tmp) : NULL;
/* ---------- /* ----------
* Free the connection info - all is in conn now * Free the option info - all is in conn now
* ---------- * ----------
*/ */
conninfo_free(); PQconninfoFree(connOptions);
/* ---------- /* ----------
* Connect to the database * Connect to the database
@ -323,20 +339,28 @@ PQconnectStart(const char *conninfo)
* PQconndefaults * PQconndefaults
* *
* Parse an empty string like PQconnectdb() would do and return the * Parse an empty string like PQconnectdb() would do and return the
* address of the connection options structure. Using this function * working connection options array.
* an application might determine all possible options and their *
* current default values. * Using this function, an application may determine all possible options
* and their current default values.
*
* NOTE: as of PostgreSQL 7.0, the returned array is dynamically allocated
* and should be freed when no longer needed via PQconninfoFree(). (In prior
* versions, the returned array was static, but that's not thread-safe.)
* Pre-7.0 applications that use this function will see a small memory leak
* until they are updated to call PQconninfoFree.
* ---------------- * ----------------
*/ */
PQconninfoOption * PQconninfoOption *
PQconndefaults(void) PQconndefaults(void)
{ {
PQExpBufferData errorBuf; PQExpBufferData errorBuf;
PQconninfoOption *connOptions;
initPQExpBuffer(&errorBuf); initPQExpBuffer(&errorBuf);
conninfo_parse("", &errorBuf); connOptions = conninfo_parse("", &errorBuf);
termPQExpBuffer(&errorBuf); termPQExpBuffer(&errorBuf);
return PQconninfoOptions; return connOptions;
} }
/* ---------------- /* ----------------
@ -565,7 +589,7 @@ update_db_info(PGconn *conn)
{ {
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
"connectDBStart() -- " "connectDBStart() -- "
"non-tcp access only possible on " "non-TCP access only possible on "
"localhost\n"); "localhost\n");
return 1; return 1;
} }
@ -2037,9 +2061,13 @@ pqPacketSend(PGconn *conn, const char *buf, size_t len)
/* ---------------- /* ----------------
* Conninfo parser routine * Conninfo parser routine
*
* If successful, a malloc'd PQconninfoOption array is returned.
* If not successful, NULL is returned and an error message is
* left in errorMessage.
* ---------------- * ----------------
*/ */
static int static PQconninfoOption *
conninfo_parse(const char *conninfo, PQExpBuffer errorMessage) conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
{ {
char *pname; char *pname;
@ -2048,16 +2076,27 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
char *tmp; char *tmp;
char *cp; char *cp;
char *cp2; char *cp2;
PQconninfoOption *options;
PQconninfoOption *option; PQconninfoOption *option;
char errortmp[INITIAL_EXPBUFFER_SIZE]; char errortmp[INITIAL_EXPBUFFER_SIZE];
conninfo_free(); /* Make a working copy of PQconninfoOptions */
options = malloc(sizeof(PQconninfoOptions));
if (options == NULL)
{
printfPQExpBuffer(errorMessage,
"FATAL: cannot allocate memory for copy of PQconninfoOptions\n");
return NULL;
}
memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions));
/* Need a modifiable copy of the input string */
if ((buf = strdup(conninfo)) == NULL) if ((buf = strdup(conninfo)) == NULL)
{ {
printfPQExpBuffer(errorMessage, printfPQExpBuffer(errorMessage,
"FATAL: cannot allocate memory for copy of conninfo string\n"); "FATAL: cannot allocate memory for copy of conninfo string\n");
return -1; PQconninfoFree(options);
return NULL;
} }
cp = buf; cp = buf;
@ -2094,10 +2133,11 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
if (*cp != '=') if (*cp != '=')
{ {
printfPQExpBuffer(errorMessage, printfPQExpBuffer(errorMessage,
"ERROR: PQconnectdb() - Missing '=' after '%s' in conninfo\n", "ERROR: Missing '=' after '%s' in conninfo\n",
pname); pname);
PQconninfoFree(options);
free(buf); free(buf);
return -1; return NULL;
} }
*cp++ = '\0'; *cp++ = '\0';
@ -2109,6 +2149,7 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
cp++; cp++;
} }
/* Get the parameter value */
pval = cp; pval = cp;
if (*cp != '\'') if (*cp != '\'')
@ -2142,8 +2183,9 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
{ {
printfPQExpBuffer(errorMessage, printfPQExpBuffer(errorMessage,
"ERROR: PQconnectdb() - unterminated quoted string in conninfo\n"); "ERROR: PQconnectdb() - unterminated quoted string in conninfo\n");
PQconninfoFree(options);
free(buf); free(buf);
return -1; return NULL;
} }
if (*cp == '\\') if (*cp == '\\')
{ {
@ -2167,27 +2209,31 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
* for the param record. * for the param record.
* ---------- * ----------
*/ */
for (option = PQconninfoOptions; option->keyword != NULL; option++) for (option = options; option->keyword != NULL; option++)
{ {
if (!strcmp(option->keyword, pname)) if (strcmp(option->keyword, pname) == 0)
break; break;
} }
if (option->keyword == NULL) if (option->keyword == NULL)
{ {
printfPQExpBuffer(errorMessage, printfPQExpBuffer(errorMessage,
"ERROR: PQconnectdb() - unknown option '%s'\n", "ERROR: Unknown conninfo option '%s'\n",
pname); pname);
PQconninfoFree(options);
free(buf); free(buf);
return -1; return NULL;
} }
/* ---------- /* ----------
* Store the value * Store the value
* ---------- * ----------
*/ */
if (option->val)
free(option->val);
option->val = strdup(pval); option->val = strdup(pval);
} }
/* Done with the modifiable input string */
free(buf); free(buf);
/* ---------- /* ----------
@ -2195,7 +2241,7 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
* in the conninfo string. * in the conninfo string.
* ---------- * ----------
*/ */
for (option = PQconninfoOptions; option->keyword != NULL; option++) for (option = options; option->keyword != NULL; option++)
{ {
if (option->val != NULL) if (option->val != NULL)
continue; /* Value was in conninfo */ continue; /* Value was in conninfo */
@ -2228,7 +2274,7 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
* Special handling for user * Special handling for user
* ---------- * ----------
*/ */
if (!strcmp(option->keyword, "user")) if (strcmp(option->keyword, "user") == 0)
{ {
option->val = fe_getauthname(errortmp); option->val = fe_getauthname(errortmp);
/* note any error message is thrown away */ /* note any error message is thrown away */
@ -2239,27 +2285,28 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
* Special handling for dbname * Special handling for dbname
* ---------- * ----------
*/ */
if (!strcmp(option->keyword, "dbname")) if (strcmp(option->keyword, "dbname") == 0)
{ {
tmp = conninfo_getval("user"); tmp = conninfo_getval(options, "user");
if (tmp) if (tmp)
option->val = strdup(tmp); option->val = strdup(tmp);
continue; continue;
} }
} }
return 0; return options;
} }
static char * static char *
conninfo_getval(char *keyword) conninfo_getval(PQconninfoOption *connOptions,
const char *keyword)
{ {
PQconninfoOption *option; PQconninfoOption *option;
for (option = PQconninfoOptions; option->keyword != NULL; option++) for (option = connOptions; option->keyword != NULL; option++)
{ {
if (!strcmp(option->keyword, keyword)) if (strcmp(option->keyword, keyword) == 0)
return option->val; return option->val;
} }
@ -2267,19 +2314,20 @@ conninfo_getval(char *keyword)
} }
static void void
conninfo_free() PQconninfoFree(PQconninfoOption *connOptions)
{ {
PQconninfoOption *option; PQconninfoOption *option;
for (option = PQconninfoOptions; option->keyword != NULL; option++) if (connOptions == NULL)
return;
for (option = connOptions; option->keyword != NULL; option++)
{ {
if (option->val != NULL) if (option->val != NULL)
{
free(option->val); free(option->val);
option->val = NULL;
}
} }
free(connOptions);
} }
/* =========== accessor functions for PGconn ========= */ /* =========== accessor functions for PGconn ========= */
@ -2350,10 +2398,9 @@ PQstatus(const PGconn *conn)
char * char *
PQerrorMessage(const PGconn *conn) PQerrorMessage(const PGconn *conn)
{ {
static char noConn[] = "PQerrorMessage: conn pointer is NULL\n";
if (!conn) if (!conn)
return noConn; return "PQerrorMessage: conn pointer is NULL\n";
return conn->errorMessage.data; return conn->errorMessage.data;
} }
@ -2452,13 +2499,15 @@ PQnoticeProcessor
PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg) PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg)
{ {
PQnoticeProcessor old; PQnoticeProcessor old;
if (conn == NULL) if (conn == NULL)
return NULL; return NULL;
old = conn->noticeHook; old = conn->noticeHook;
if (proc) { if (proc)
conn->noticeHook = proc; {
conn->noticeArg = arg; conn->noticeHook = proc;
conn->noticeArg = arg;
} }
return old; return old;
} }

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.91 2000/02/24 04:50:51 tgl Exp $ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.92 2000/03/11 03:08:36 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -39,6 +39,7 @@ char * const pgresStatus[] = {
}; };
/* Note: DONOTICE macro will work if applied to either PGconn or PGresult */
#define DONOTICE(conn,message) \ #define DONOTICE(conn,message) \
((*(conn)->noticeHook) ((conn)->noticeArg, (message))) ((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
@ -135,7 +136,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
result = (PGresult *) malloc(sizeof(PGresult)); result = (PGresult *) malloc(sizeof(PGresult));
result->conn = conn; /* might be NULL */ result->xconn = conn; /* might be NULL */
result->ntups = 0; result->ntups = 0;
result->numAttributes = 0; result->numAttributes = 0;
result->attDescs = NULL; result->attDescs = NULL;
@ -150,8 +151,11 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
result->curOffset = 0; result->curOffset = 0;
result->spaceLeft = 0; result->spaceLeft = 0;
if (conn) /* consider copying conn's errorMessage */ if (conn)
{ {
result->noticeHook = conn->noticeHook;
result->noticeArg = conn->noticeArg;
/* consider copying conn's errorMessage */
switch (status) switch (status)
{ {
case PGRES_EMPTY_QUERY: case PGRES_EMPTY_QUERY:
@ -166,6 +170,12 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
break; break;
} }
} }
else
{
result->noticeHook = NULL;
result->noticeArg = NULL;
}
return result; return result;
} }
@ -1833,12 +1843,12 @@ check_field_number(const char *routineName, const PGresult *res, int field_num)
return FALSE; /* no way to display error message... */ return FALSE; /* no way to display error message... */
if (field_num < 0 || field_num >= res->numAttributes) if (field_num < 0 || field_num >= res->numAttributes)
{ {
if (res->conn) if (res->noticeHook)
{ {
sprintf(noticeBuf, sprintf(noticeBuf,
"%s: ERROR! field number %d is out of range 0..%d\n", "%s: ERROR! field number %d is out of range 0..%d\n",
routineName, field_num, res->numAttributes - 1); routineName, field_num, res->numAttributes - 1);
DONOTICE(res->conn, noticeBuf); DONOTICE(res, noticeBuf);
} }
return FALSE; return FALSE;
} }
@ -1855,23 +1865,23 @@ check_tuple_field_number(const char *routineName, const PGresult *res,
return FALSE; /* no way to display error message... */ return FALSE; /* no way to display error message... */
if (tup_num < 0 || tup_num >= res->ntups) if (tup_num < 0 || tup_num >= res->ntups)
{ {
if (res->conn) if (res->noticeHook)
{ {
sprintf(noticeBuf, sprintf(noticeBuf,
"%s: ERROR! tuple number %d is out of range 0..%d\n", "%s: ERROR! tuple number %d is out of range 0..%d\n",
routineName, tup_num, res->ntups - 1); routineName, tup_num, res->ntups - 1);
DONOTICE(res->conn, noticeBuf); DONOTICE(res, noticeBuf);
} }
return FALSE; return FALSE;
} }
if (field_num < 0 || field_num >= res->numAttributes) if (field_num < 0 || field_num >= res->numAttributes)
{ {
if (res->conn) if (res->noticeHook)
{ {
sprintf(noticeBuf, sprintf(noticeBuf,
"%s: ERROR! field number %d is out of range 0..%d\n", "%s: ERROR! field number %d is out of range 0..%d\n",
routineName, field_num, res->numAttributes - 1); routineName, field_num, res->numAttributes - 1);
DONOTICE(res->conn, noticeBuf); DONOTICE(res, noticeBuf);
} }
return FALSE; return FALSE;
} }
@ -1982,11 +1992,11 @@ PQcmdStatus(PGresult *res)
char * char *
PQoidStatus(const PGresult *res) PQoidStatus(const PGresult *res)
{ {
/* /*
* This must be enough to hold the result. Don't laugh, this is * This must be enough to hold the result. Don't laugh, this is
* better than what this function used to do. * better than what this function used to do.
*/ */
static char buf[24]; static char buf[24];
size_t len; size_t len;
@ -1995,7 +2005,7 @@ PQoidStatus(const PGresult *res)
len = strspn(res->cmdStatus + 7, "0123456789"); len = strspn(res->cmdStatus + 7, "0123456789");
if (len > 23) if (len > 23)
len = 23; len = 23;
strncpy(buf, res->cmdStatus + 7, len); strncpy(buf, res->cmdStatus + 7, len);
buf[23] = '\0'; buf[23] = '\0';
@ -2046,12 +2056,12 @@ PQcmdTuples(PGresult *res)
if (*p == 0) if (*p == 0)
{ {
if (res->conn) if (res->noticeHook)
{ {
sprintf(noticeBuf, sprintf(noticeBuf,
"PQcmdTuples (%s) -- bad input from server\n", "PQcmdTuples (%s) -- bad input from server\n",
res->cmdStatus); res->cmdStatus);
DONOTICE(res->conn, noticeBuf); DONOTICE(res, noticeBuf);
} }
return ""; return "";
} }
@ -2062,11 +2072,11 @@ PQcmdTuples(PGresult *res)
p++; /* INSERT: skip oid */ p++; /* INSERT: skip oid */
if (*p == 0) if (*p == 0)
{ {
if (res->conn) if (res->noticeHook)
{ {
sprintf(noticeBuf, sprintf(noticeBuf,
"PQcmdTuples (INSERT) -- there's no # of tuples\n"); "PQcmdTuples (INSERT) -- there's no # of tuples\n");
DONOTICE(res->conn, noticeBuf); DONOTICE(res, noticeBuf);
} }
return ""; return "";
} }

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: libpq-fe.h,v 1.60 2000/02/07 23:10:11 petere Exp $ * $Id: libpq-fe.h,v 1.61 2000/03/11 03:08:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -130,6 +130,10 @@ extern "C"
/* ---------------- /* ----------------
* Structure for the conninfo parameter definitions returned by PQconndefaults * Structure for the conninfo parameter definitions returned by PQconndefaults
*
* All fields except "val" point at static strings which must not be altered.
* "val" is either NULL or a malloc'd current-value string. PQconninfoFree()
* will release both the val strings and the PQconninfoOption array itself.
* ---------------- * ----------------
*/ */
typedef struct _PQconninfoOption typedef struct _PQconninfoOption
@ -137,14 +141,14 @@ extern "C"
char *keyword; /* The keyword of the option */ char *keyword; /* The keyword of the option */
char *envvar; /* Fallback environment variable name */ char *envvar; /* Fallback environment variable name */
char *compiled; /* Fallback compiled in default value */ char *compiled; /* Fallback compiled in default value */
char *val; /* Options value */ char *val; /* Option's current value, or NULL */
char *label; /* Label for field in connect dialog */ char *label; /* Label for field in connect dialog */
char *dispchar; /* Character to display for this field */ char *dispchar; /* Character to display for this field
/* in a connect dialog. Values are: */ * in a connect dialog. Values are:
/* "" Display entered value as is */ * "" Display entered value as is
/* "*" Password field - hide value */ * "*" Password field - hide value
/* "D" Debug options - don't */ * "D" Debug option - don't show by default
/* create a field by default */ */
int dispsize; /* Field size in characters for dialog */ int dispsize; /* Field size in characters for dialog */
} PQconninfoOption; } PQconninfoOption;
@ -183,11 +187,14 @@ extern "C"
#define PQsetdb(M_PGHOST,M_PGPORT,M_PGOPT,M_PGTTY,M_DBNAME) \ #define PQsetdb(M_PGHOST,M_PGPORT,M_PGOPT,M_PGTTY,M_DBNAME) \
PQsetdbLogin(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME, NULL, NULL) PQsetdbLogin(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME, NULL, NULL)
/* close the current connection and free the PGconn data structure */
extern void PQfinish(PGconn *conn);
/* get info about connection options known to PQconnectdb */ /* get info about connection options known to PQconnectdb */
extern PQconninfoOption *PQconndefaults(void); extern PQconninfoOption *PQconndefaults(void);
/* close the current connection and free the PGconn data structure */ /* free the data structure returned by PQconndefaults() */
extern void PQfinish(PGconn *conn); extern void PQconninfoFree(PQconninfoOption *connOptions);
/* /*
* close the current connection and restablish a new one with the same * close the current connection and restablish a new one with the same

View File

@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: libpq-int.h,v 1.19 2000/02/07 23:10:11 petere Exp $ * $Id: libpq-int.h,v 1.20 2000/03/11 03:08:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -121,7 +121,21 @@ struct pg_result
* last query */ * last query */
int binary; /* binary tuple values if binary == 1, int binary; /* binary tuple values if binary == 1,
* otherwise ASCII */ * otherwise ASCII */
PGconn *conn; /* connection we did the query on, if any */ /*
* The conn link in PGresult is no longer used by any libpq code.
* It should be removed entirely, because it could be a dangling link
* (the application could keep the PGresult around longer than it keeps
* the PGconn!) But there may be apps out there that depend on it,
* so we will leave it here at least for a release or so.
*/
PGconn *xconn; /* connection we did the query on, if any */
/* Callback procedure for notice/error message processing
* (copied from originating PGconn).
*/
PQnoticeProcessor noticeHook;
void *noticeArg;
char *errMsg; /* error message, or NULL if no error */ char *errMsg; /* error message, or NULL if no error */
/* All NULL attributes in the query result point to this null string */ /* All NULL attributes in the query result point to this null string */

View File

@ -78,3 +78,4 @@ EXPORTS
appendPQExpBufferStr @ 75 appendPQExpBufferStr @ 75
destroyPQExpBuffer @ 76 destroyPQExpBuffer @ 76
createPQExpBuffer @ 77 createPQExpBuffer @ 77
PQconninfoFree @ 78

View File

@ -1,6 +1,6 @@
/*------------------------------------------------------- /*-------------------------------------------------------
* *
* $Id: Pg.xs,v 1.13 1999/10/13 02:26:37 momjian Exp $ with patch for NULs * $Id: Pg.xs,v 1.14 2000/03/11 03:08:37 tgl Exp $ with patch for NULs
* *
* Copyright (c) 1997, 1998 Edmund Mergl * Copyright (c) 1997, 1998 Edmund Mergl
* *
@ -247,17 +247,18 @@ PQsetdb(pghost, pgport, pgoptions, pgtty, dbname)
HV * HV *
PQconndefaults() PQconndefaults()
CODE: CODE:
PQconninfoOption *infoOption; PQconninfoOption *infoOptions;
RETVAL = newHV(); RETVAL = newHV();
if (infoOption = PQconndefaults()) { if (infoOptions = PQconndefaults()) {
while (infoOption->keyword != NULL) { PQconninfoOption *option;
if (infoOption->val != NULL) { for (option = infoOptions; option->keyword != NULL; option++) {
hv_store(RETVAL, infoOption->keyword, strlen(infoOption->keyword), newSVpv(infoOption->val, 0), 0); if (option->val != NULL) {
hv_store(RETVAL, option->keyword, strlen(option->keyword), newSVpv(option->val, 0), 0);
} else { } else {
hv_store(RETVAL, infoOption->keyword, strlen(infoOption->keyword), newSVpv("", 0), 0); hv_store(RETVAL, option->keyword, strlen(option->keyword), newSVpv("", 0), 0);
} }
infoOption++;
} }
PQconninfoFree(infoOptions);
} }
OUTPUT: OUTPUT:
RETVAL RETVAL
@ -774,17 +775,18 @@ setdb(pghost, pgport, pgoptions, pgtty, dbname)
HV * HV *
conndefaults() conndefaults()
CODE: CODE:
PQconninfoOption *infoOption; PQconninfoOption *infoOptions;
RETVAL = newHV(); RETVAL = newHV();
if (infoOption = PQconndefaults()) { if (infoOptions = PQconndefaults()) {
while (infoOption->keyword != NULL) { PQconninfoOption *option;
if (infoOption->val != NULL) { for (option = infoOptions; option->keyword != NULL; option++) {
hv_store(RETVAL, infoOption->keyword, strlen(infoOption->keyword), newSVpv(infoOption->val, 0), 0); if (option->val != NULL) {
hv_store(RETVAL, option->keyword, strlen(option->keyword), newSVpv(option->val, 0), 0);
} else { } else {
hv_store(RETVAL, infoOption->keyword, strlen(infoOption->keyword), newSVpv("", 0), 0); hv_store(RETVAL, option->keyword, strlen(option->keyword), newSVpv("", 0), 0);
} }
infoOption++;
} }
PQconninfoFree(infoOptions);
} }
OUTPUT: OUTPUT:
RETVAL RETVAL