mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-29 22:49:41 +03:00 
			
		
		
		
	Attached is a patch to remove the definitions of libpq's internal
structs from libpq-fe.h, as we previously discussed. There turned out to be sloppy coding practices in more places than I had realized :-(, but all in all I think it was a well-worth-while exercise. I ended up adding several routines to libpq's API in order to respond to application requirements that were exposed by this work. I owe the docs crew updates for libpq.sgml to describe these changes. I'm way too tired to work on the docs tonight, however. This is the last major change I intend to submit for 6.4. I do want to see if I can make libpgtcl work with Tcl 8.0 before we go final, but hopefully that will be a minor bug fix.
This commit is contained in:
		| @@ -7,7 +7,7 @@ | ||||
|  * | ||||
|  * | ||||
|  * IDENTIFICATION | ||||
|  *	  $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclCmds.c,v 1.31 1998/09/01 04:39:56 momjian Exp $ | ||||
|  *	  $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclCmds.c,v 1.32 1998/09/03 02:10:42 momjian Exp $ | ||||
|  * | ||||
|  *------------------------------------------------------------------------- | ||||
|  */ | ||||
| @@ -358,15 +358,15 @@ Pg_connect(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) | ||||
| 		conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName); | ||||
| 	} | ||||
|  | ||||
| 	if (conn->status == CONNECTION_OK) | ||||
|     if (PQstatus(conn) == CONNECTION_OK) { | ||||
| 	{ | ||||
| 		PgSetConnectionId(interp, conn); | ||||
| 		return TCL_OK; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		Tcl_AppendResult(interp, "Connection to database failed\n", 0); | ||||
| 		Tcl_AppendResult(interp, conn->errorMessage, 0); | ||||
| 		Tcl_AppendResult(interp, "Connection to database failed\n", | ||||
| 			PQerrorMessage(conn), 0); | ||||
| 		PQfinish(conn); | ||||
| 		return TCL_ERROR; | ||||
| 	} | ||||
| @@ -423,7 +423,6 @@ Pg_exec(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) | ||||
| 	Pg_ConnectionId *connid; | ||||
| 	PGconn	   *conn; | ||||
| 	PGresult   *result; | ||||
| 	int			connStatus; | ||||
|  | ||||
| 	if (argc != 3) | ||||
| 	{ | ||||
| @@ -442,7 +441,6 @@ Pg_exec(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) | ||||
| 		return TCL_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	connStatus = conn->status; | ||||
| 	result = PQexec(conn, argv[2]); | ||||
|  | ||||
| 	/* Transfer any notify events from libpq to Tcl event queue. */ | ||||
| @@ -452,8 +450,8 @@ Pg_exec(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) | ||||
| 	{ | ||||
| 		int			rId = PgSetResultId(interp, argv[1], result); | ||||
|  | ||||
| 		if (result->resultStatus == PGRES_COPY_IN || | ||||
| 			result->resultStatus == PGRES_COPY_OUT) | ||||
| 		ExecStatusType rStat = PQresultStatus(result); | ||||
| 		if (rStat == PGRES_COPY_IN || rStat == PGRES_COPY_OUT) | ||||
| 		{ | ||||
| 			connid->res_copyStatus = RES_COPY_INPROGRESS; | ||||
| 			connid->res_copy = rId; | ||||
| @@ -463,7 +461,7 @@ Pg_exec(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) | ||||
| 	else | ||||
| 	{ | ||||
| 		/* error occurred during the query */ | ||||
| 		Tcl_SetResult(interp, conn->errorMessage, TCL_VOLATILE); | ||||
| 		Tcl_SetResult(interp, PQerrorMessage(conn), TCL_VOLATILE); | ||||
| 		return TCL_ERROR; | ||||
| 	} | ||||
| } | ||||
| @@ -481,9 +479,12 @@ Pg_exec(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) | ||||
|  -conn | ||||
|  the connection that produced the result | ||||
|  -assign arrayName | ||||
|  assign the results to an array | ||||
|  -assignbyidx arrayName | ||||
|  assign the results to an array using the first field as a key | ||||
|  assign the results to an array, using subscripts of the form | ||||
|  (tupno,attributeName) | ||||
|  -assignbyidx arrayName ?appendstr? | ||||
|  assign the results to an array using the first field's value as a key. | ||||
|  All but the first field of each tuple are stored, using subscripts of the form | ||||
|  (field0value,attributeNameappendstr) | ||||
|  -numTuples | ||||
|  the number of tuples in the query | ||||
|  -attributes | ||||
| @@ -509,6 +510,7 @@ Pg_result(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) | ||||
| 	int			tupno; | ||||
| 	char	   *arrVar; | ||||
| 	char		nameBuffer[256]; | ||||
|     const char *appendstr; | ||||
|  | ||||
| 	if (argc < 3 || argc > 5) | ||||
| 	{ | ||||
| @@ -564,8 +566,9 @@ Pg_result(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) | ||||
|  | ||||
| 		/* | ||||
| 		 * 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). Note we expect field names not to | ||||
| 		 * array with the name given in the argument. | ||||
| 		 * The indices of the array are of the form (tupno,attrName). | ||||
| 		 * Note we expect field names not to | ||||
| 		 * exceed a few dozen characters, so truncating to prevent buffer | ||||
| 		 * overflow shouldn't be a problem. | ||||
| 		 */ | ||||
| @@ -589,28 +592,32 @@ Pg_result(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) | ||||
| 	} | ||||
| 	else if (strcmp(opt, "-assignbyidx") == 0) | ||||
| 	{ | ||||
| 		if (argc != 4) | ||||
| 		if (argc != 4 && argc != 5) | ||||
| 		{ | ||||
| 			Tcl_AppendResult(interp, "-assignbyidx option must be followed by a variable name", 0); | ||||
| 			Tcl_AppendResult(interp, "-assignbyidx option requires an array name and optionally an append string",0); | ||||
| 			return TCL_ERROR; | ||||
| 		} | ||||
| 		arrVar = argv[3]; | ||||
| 		appendstr = (argc == 5) ? (const char *) argv[4] : ""; | ||||
|  | ||||
| 		/* | ||||
| 		 * 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). Here, we still assume PQfname won't | ||||
| 		 * exceed 200 characters, but we dare not make the same assumption | ||||
| 		 * about the data in field 0. | ||||
| 		 * array with the name given in the argument.  The indices of the array | ||||
| 		 * are of the form (field0Value,attrNameappendstr). | ||||
| 		 * Here, we still assume PQfname won't exceed 200 characters, | ||||
| 		 * but we dare not make the same assumption about the data in field 0 | ||||
| 		 * nor the append string. | ||||
| 		 */ | ||||
| 		for (tupno = 0; tupno < PQntuples(result); tupno++) | ||||
| 		{ | ||||
| 			const char *field0 = PQgetvalue(result, tupno, 0); | ||||
| 			char	   *workspace = malloc(strlen(field0) + 210); | ||||
| 			char * workspace = malloc(strlen(field0) + strlen(appendstr) + 210); | ||||
|  | ||||
| 			for (i = 1; i < PQnfields(result); i++) | ||||
| 			{ | ||||
| 				sprintf(workspace, "%s,%.200s", field0, PQfname(result, i)); | ||||
| 				sprintf(workspace, "%s,%.200s%s", field0, PQfname(result,i), | ||||
| 					appendstr); | ||||
| 				sprintf(workspace, "%s,%.200s", field0, PQfname(result,i)); | ||||
| 				if (Tcl_SetVar2(interp, arrVar, workspace, | ||||
| 								PQgetvalue(result, tupno, i), | ||||
| 								TCL_LEAVE_ERR_MSG) == NULL) | ||||
| @@ -701,7 +708,7 @@ Pg_result_errReturn: | ||||
| 					 "\t-status\n", | ||||
| 					 "\t-conn\n", | ||||
| 					 "\t-assign arrayVarName\n", | ||||
| 					 "\t-assignbyidx arrayVarName\n", | ||||
| 					 "\t-assignbyidx arrayVarName ?appendstr?\n", | ||||
| 					 "\t-numTuples\n", | ||||
| 					 "\t-numAttrs\n" | ||||
| 					 "\t-attributes\n" | ||||
| @@ -1238,7 +1245,7 @@ Pg_select(ClientData cData, Tcl_Interp * interp, int argc, char **argv) | ||||
| 	if ((result = PQexec(conn, argv[2])) == 0) | ||||
| 	{ | ||||
| 		/* error occurred during the query */ | ||||
| 		Tcl_SetResult(interp, conn->errorMessage, TCL_STATIC); | ||||
| 		Tcl_SetResult(interp, PQerrorMessage(conn), TCL_STATIC); | ||||
| 		return TCL_ERROR; | ||||
| 	} | ||||
|  | ||||
| @@ -1406,11 +1413,10 @@ Pg_listen(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) | ||||
| 			ckfree(cmd); | ||||
| 			/* Transfer any notify events from libpq to Tcl event queue. */ | ||||
| 			PgNotifyTransferEvents(connid); | ||||
| 			if (!result || (result->resultStatus != PGRES_COMMAND_OK)) | ||||
| 			if (PQresultStatus(result) != PGRES_COMMAND_OK) { | ||||
| 			{ | ||||
| 				/* Error occurred during the execution of command */ | ||||
| 				if (result) | ||||
| 					PQclear(result); | ||||
| 				PQclear(result); | ||||
| 				ckfree(callback); | ||||
| 				ckfree(caserelname); | ||||
| 				Tcl_DeleteHashEntry(entry); | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
|  * | ||||
|  * | ||||
|  * IDENTIFICATION | ||||
|  *	  $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclId.c,v 1.14 1998/09/01 04:39:58 momjian Exp $ | ||||
|  *	  $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclId.c,v 1.15 1998/09/03 02:10:44 momjian Exp $ | ||||
|  * | ||||
|  *------------------------------------------------------------------------- | ||||
|  */ | ||||
| @@ -33,98 +33,62 @@ PgEndCopy(Pg_ConnectionId * connid, int *errorCodePtr) | ||||
| 	connid->res_copyStatus = RES_COPY_NONE; | ||||
| 	if (PQendcopy(connid->conn)) | ||||
| 	{ | ||||
| 		connid->results[connid->res_copy]->resultStatus = PGRES_BAD_RESPONSE; | ||||
| 		PQclear(connid->results[connid->res_copy]); | ||||
| 		connid->results[connid->res_copy] = | ||||
| 		PQmakeEmptyPGresult(connid->conn, PGRES_BAD_RESPONSE); | ||||
| 		connid->res_copy = -1; | ||||
| 		*errorCodePtr = EIO; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		connid->results[connid->res_copy]->resultStatus = PGRES_COMMAND_OK; | ||||
| 		PQclear(connid->results[connid->res_copy]); | ||||
| 		connid->results[connid->res_copy] = | ||||
| 		PQmakeEmptyPGresult(connid->conn, PGRES_COMMAND_OK); | ||||
| 		connid->res_copy = -1; | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* | ||||
|  *	Called when reading data (via gets) for a copy <rel> to stdout. | ||||
|  * | ||||
|  *	NOTE: this routine knows way more than it ought to about libpq's | ||||
|  *	internal buffering mechanisms. | ||||
|  *  Called when reading data (via gets) for a copy <rel> to stdout. | ||||
|  */ | ||||
| int | ||||
| PgInputProc(DRIVER_INPUT_PROTO) | ||||
| int PgInputProc(DRIVER_INPUT_PROTO) | ||||
| { | ||||
| 	Pg_ConnectionId *connid; | ||||
| 	PGconn	   *conn; | ||||
| 	char		c; | ||||
| 	int			avail; | ||||
|     Pg_ConnectionId	*connid; | ||||
|     PGconn		*conn; | ||||
|     int			avail; | ||||
|  | ||||
| 	connid = (Pg_ConnectionId *) cData; | ||||
| 	conn = connid->conn; | ||||
|     connid = (Pg_ConnectionId *)cData; | ||||
|     conn = connid->conn; | ||||
|  | ||||
| 	if (connid->res_copy < 0 || | ||||
| 		connid->results[connid->res_copy]->resultStatus != PGRES_COPY_OUT) | ||||
|     if (connid->res_copy < 0 || | ||||
| 	PQresultStatus(connid->results[connid->res_copy]) != PGRES_COPY_OUT) | ||||
| 	{ | ||||
| 		*errorCodePtr = EBUSY; | ||||
| 		return -1; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
| 	/* Try to load any newly arrived data */ | ||||
| 	conn->errorMessage[0] = '\0'; | ||||
| 	PQconsumeInput(conn); | ||||
| 	if (conn->errorMessage[0]) | ||||
|     /* Read any newly arrived data into libpq's buffer, | ||||
|      * thereby clearing the socket's read-ready condition. | ||||
|      */ | ||||
|     if (! PQconsumeInput(conn)) | ||||
| 	{ | ||||
| 		*errorCodePtr = EIO; | ||||
| 		return -1; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
| 	/* | ||||
| 	 * Move data from libpq's buffer to Tcl's. We want to accept data only | ||||
| 	 * in units of whole lines, not partial lines.	This ensures that we | ||||
| 	 * can recognize the terminator line "\\.\n".  (Otherwise, if it | ||||
| 	 * happened to cross a packet/buffer boundary, we might hand the first | ||||
| 	 * one or two characters off to Tcl, which we shouldn't.) | ||||
| 	 */ | ||||
|     /* Move data from libpq's buffer to Tcl's. */ | ||||
|  | ||||
| 	conn->inCursor = conn->inStart; | ||||
|     avail = PQgetlineAsync(conn, buf, bufSize); | ||||
|  | ||||
| 	avail = bufSize; | ||||
| 	while (avail > 0 && conn->inCursor < conn->inEnd) | ||||
|     if (avail < 0) | ||||
| 	{ | ||||
| 		c = conn->inBuffer[conn->inCursor++]; | ||||
| 		*buf++ = c; | ||||
| 		--avail; | ||||
| 		if (c == '\n') | ||||
| 		{ | ||||
| 			/* Got a complete line; mark the data removed from libpq */ | ||||
| 			conn->inStart = conn->inCursor; | ||||
| 			/* Is it the endmarker line? */ | ||||
| 			if (bufSize - avail == 3 && buf[-3] == '\\' && buf[-2] == '.') | ||||
| 			{ | ||||
| 				/* Yes, change state and return 0 */ | ||||
| 				return PgEndCopy(connid, errorCodePtr); | ||||
| 			} | ||||
| 			/* No, return the data to Tcl */ | ||||
| 			/* fprintf(stderr, "returning %d chars\n", bufSize - avail); */ | ||||
| 			return bufSize - avail; | ||||
| 		} | ||||
| 	} | ||||
|       /* Endmarker detected, change state and return 0 */ | ||||
|       return PgEndCopy(connid, errorCodePtr); | ||||
|     } | ||||
|  | ||||
| 	/* | ||||
| 	 * We don't have a complete line. We'd prefer to leave it in libpq's | ||||
| 	 * buffer until the rest arrives, but there is a special case: what if | ||||
| 	 * the line is longer than the buffer Tcl is offering us?  In that | ||||
| 	 * case we'd better hand over a partial line, else we'd get into an | ||||
| 	 * infinite loop. Do this in a way that ensures we can't misrecognize | ||||
| 	 * a terminator line later: leave last 3 characters in libpq buffer. | ||||
| 	 */ | ||||
| 	if (avail == 0 && bufSize > 3) | ||||
| 	{ | ||||
| 		conn->inStart = conn->inCursor - 3; | ||||
| 		return bufSize - 3; | ||||
| 	} | ||||
| 	return 0; | ||||
|     return avail; | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -140,17 +104,13 @@ PgOutputProc(DRIVER_OUTPUT_PROTO) | ||||
| 	conn = connid->conn; | ||||
|  | ||||
| 	if (connid->res_copy < 0 || | ||||
| 		connid->results[connid->res_copy]->resultStatus != PGRES_COPY_IN) | ||||
| 		PQresultStatus(connid->results[connid->res_copy]) != PGRES_COPY_IN) | ||||
| 	{ | ||||
| 		*errorCodePtr = EBUSY; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	conn->errorMessage[0] = '\0'; | ||||
|  | ||||
| 	PQputnbytes(conn, buf, bufSize); | ||||
|  | ||||
| 	if (conn->errorMessage[0]) | ||||
|     if (PQputnbytes(conn, buf, bufSize)) | ||||
| 	{ | ||||
| 		*errorCodePtr = EIO; | ||||
| 		return -1; | ||||
| @@ -398,7 +358,7 @@ getresid(Tcl_Interp * interp, char *id, Pg_ConnectionId ** connid_p) | ||||
|  | ||||
| 	connid = (Pg_ConnectionId *) Tcl_GetChannelInstanceData(conn_chan); | ||||
|  | ||||
| 	if (resid < 0 || resid > connid->res_max || connid->results[resid] == NULL) | ||||
|     if (resid < 0 || resid >= connid->res_max || connid->results[resid] == NULL) | ||||
| 	{ | ||||
| 		Tcl_SetResult(interp, "Invalid result handle", TCL_STATIC); | ||||
| 		return -1; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user