⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fe-exec.c

📁 关系型数据库 Postgresql 6.5.2
💻 C
📖 第 1 页 / 共 4 页
字号:
/*------------------------------------------------------------------------- * * fe-exec.c *	  functions related to sending a query down to the backend * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $Header: /usr/local/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.81 1999/05/28 01:54:53 tgl Exp $ * *------------------------------------------------------------------------- */#include "libpq-fe.h"#include "libpq-int.h"#include "postgres.h"#ifdef WIN32#include "win32.h"#else#if !defined(NO_UNISTD_H)#include <unistd.h>#endif#endif#include <stdlib.h>#include <string.h>#include <errno.h>#include <ctype.h>/* keep this in same order as ExecStatusType in libpq-fe.h */const char *const pgresStatus[] = {	"PGRES_EMPTY_QUERY",	"PGRES_COMMAND_OK",	"PGRES_TUPLES_OK",	"PGRES_COPY_OUT",	"PGRES_COPY_IN",	"PGRES_BAD_RESPONSE",	"PGRES_NONFATAL_ERROR",	"PGRES_FATAL_ERROR"};#define DONOTICE(conn,message) \	((*(conn)->noticeHook) ((conn)->noticeArg, (message)))static int	addTuple(PGresult *res, PGresAttValue *tup);static void parseInput(PGconn *conn);static void handleSendFailure(PGconn *conn);static int	getRowDescriptions(PGconn *conn);static int	getAnotherTuple(PGconn *conn, int binary);static int	getNotify(PGconn *conn);static int	getNotice(PGconn *conn);/* ---------------- * Space management for PGresult. * * Formerly, libpq did a separate malloc() for each field of each tuple * returned by a query.  This was remarkably expensive --- malloc/free * consumed a sizable part of the application's runtime.  And there is * no real need to keep track of the fields separately, since they will * all be freed together when the PGresult is released.  So now, we grab * large blocks of storage from malloc and allocate space for query data * within these blocks, using a trivially simple allocator.  This reduces * the number of malloc/free calls dramatically, and it also avoids * fragmentation of the malloc storage arena. * The PGresult structure itself is still malloc'd separately.  We could * combine it with the first allocation block, but that would waste space * for the common case that no extra storage is actually needed (that is, * the SQL command did not return tuples). * We also malloc the top-level array of tuple pointers separately, because * we need to be able to enlarge it via realloc, and our trivial space * allocator doesn't handle that effectively.  (Too bad the FE/BE protocol * doesn't tell us up front how many tuples will be returned.) * All other subsidiary storage for a PGresult is kept in PGresult_data blocks * of size PGRESULT_DATA_BLOCKSIZE.  The overhead at the start of each block * is just a link to the next one, if any.	Free-space management info is * kept in the owning PGresult. * A query returning a small amount of data will thus require three malloc * calls: one for the PGresult, one for the tuples pointer array, and one * PGresult_data block. * Only the most recently allocated PGresult_data block is a candidate to * have more stuff added to it --- any extra space left over in older blocks * is wasted.  We could be smarter and search the whole chain, but the point * here is to be simple and fast.  Typical applications do not keep a PGresult * around very long anyway, so some wasted space within one is not a problem. * * Tuning constants for the space allocator are: * PGRESULT_DATA_BLOCKSIZE: size of a standard allocation block, in bytes * PGRESULT_ALIGN_BOUNDARY: assumed alignment requirement for binary data * PGRESULT_SEP_ALLOC_THRESHOLD: objects bigger than this are given separate *	 blocks, instead of being crammed into a regular allocation block. * Requirements for correct function are: * PGRESULT_ALIGN_BOUNDARY must be a multiple of the alignment requirements *		of all machine data types.	(Currently this is set from configure *		tests, so it should be OK automatically.) * PGRESULT_SEP_ALLOC_THRESHOLD + PGRESULT_BLOCK_OVERHEAD <= *			PGRESULT_DATA_BLOCKSIZE *		pqResultAlloc assumes an object smaller than the threshold will fit *		in a new block. * The amount of space wasted at the end of a block could be as much as * PGRESULT_SEP_ALLOC_THRESHOLD, so it doesn't pay to make that too large. * ---------------- */#ifdef MAX#undef MAX#endif#define MAX(a,b)  ((a) > (b) ? (a) : (b))#define PGRESULT_DATA_BLOCKSIZE		2048#define PGRESULT_ALIGN_BOUNDARY		MAXIMUM_ALIGNOF		/* from configure */#define PGRESULT_BLOCK_OVERHEAD		MAX(sizeof(PGresult_data), PGRESULT_ALIGN_BOUNDARY)#define PGRESULT_SEP_ALLOC_THRESHOLD	(PGRESULT_DATA_BLOCKSIZE / 2)/* * PQmakeEmptyPGresult *	 returns a newly allocated, initialized PGresult with given status. *	 If conn is not NULL and status indicates an error, the conn's *	 errorMessage is copied. * * Note this is exported --- you wouldn't think an application would need * to build its own PGresults, but this has proven useful in both libpgtcl * and the Perl5 interface, so maybe it's not so unreasonable. */PGresult   *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status){	PGresult   *result;	result = (PGresult *) malloc(sizeof(PGresult));	result->conn = conn;		/* might be NULL */	result->ntups = 0;	result->numAttributes = 0;	result->attDescs = NULL;	result->tuples = NULL;	result->tupArrSize = 0;	result->resultStatus = status;	result->cmdStatus[0] = '\0';	result->binary = 0;	result->errMsg = NULL;	result->null_field[0] = '\0';	result->curBlock = NULL;	result->curOffset = 0;	result->spaceLeft = 0;	if (conn)					/* consider copying conn's errorMessage */	{		switch (status)		{			case PGRES_EMPTY_QUERY:			case PGRES_COMMAND_OK:			case PGRES_TUPLES_OK:			case PGRES_COPY_OUT:			case PGRES_COPY_IN:				/* non-error cases */				break;			default:				pqSetResultError(result, conn->errorMessage);				break;		}	}	return result;}/* * pqResultAlloc - *		Allocate subsidiary storage for a PGresult. * * nBytes is the amount of space needed for the object. * If isBinary is true, we assume that we need to align the object on * a machine allocation boundary. * If isBinary is false, we assume the object is a char string and can * be allocated on any byte boundary. */void *pqResultAlloc(PGresult *res, int nBytes, int isBinary){	char	   *space;	PGresult_data *block;	if (!res)		return NULL;	if (nBytes <= 0)		return res->null_field;	/*	 * If alignment is needed, round up the current position to an	 * alignment boundary.	 */	if (isBinary)	{		int			offset = res->curOffset % PGRESULT_ALIGN_BOUNDARY;		if (offset)		{			res->curOffset += PGRESULT_ALIGN_BOUNDARY - offset;			res->spaceLeft -= PGRESULT_ALIGN_BOUNDARY - offset;		}	}	/* If there's enough space in the current block, no problem. */	if (nBytes <= res->spaceLeft)	{		space = res->curBlock->space + res->curOffset;		res->curOffset += nBytes;		res->spaceLeft -= nBytes;		return space;	}	/*	 * If the requested object is very large, give it its own block; this	 * avoids wasting what might be most of the current block to start a	 * new block.  (We'd have to special-case requests bigger than the	 * block size anyway.)	The object is always given binary alignment in	 * this case.	 */	if (nBytes >= PGRESULT_SEP_ALLOC_THRESHOLD)	{		block = (PGresult_data *) malloc(nBytes + PGRESULT_BLOCK_OVERHEAD);		if (!block)			return NULL;		space = block->space + PGRESULT_BLOCK_OVERHEAD;		if (res->curBlock)		{			/*			 * Tuck special block below the active block, so that we don't			 * have to waste the free space in the active block.			 */			block->next = res->curBlock->next;			res->curBlock->next = block;		}		else		{			/* Must set up the new block as the first active block. */			block->next = NULL;			res->curBlock = block;			res->spaceLeft = 0; /* be sure it's marked full */		}		return space;	}	/* Otherwise, start a new block. */	block = (PGresult_data *) malloc(PGRESULT_DATA_BLOCKSIZE);	if (!block)		return NULL;	block->next = res->curBlock;	res->curBlock = block;	if (isBinary)	{		/* object needs full alignment */		res->curOffset = PGRESULT_BLOCK_OVERHEAD;		res->spaceLeft = PGRESULT_DATA_BLOCKSIZE - PGRESULT_BLOCK_OVERHEAD;	}	else	{		/* we can cram it right after the overhead pointer */		res->curOffset = sizeof(PGresult_data);		res->spaceLeft = PGRESULT_DATA_BLOCKSIZE - sizeof(PGresult_data);	}	space = block->space + res->curOffset;	res->curOffset += nBytes;	res->spaceLeft -= nBytes;	return space;}/* * pqResultStrdup - *		Like strdup, but the space is subsidiary PGresult space. */char *pqResultStrdup(PGresult *res, const char *str){	char	   *space = (char *) pqResultAlloc(res, strlen(str) + 1, FALSE);	if (space)		strcpy(space, str);	return space;}/* * pqSetResultError - *		assign a new error message to a PGresult */voidpqSetResultError(PGresult *res, const char *msg){	if (!res)		return;	if (msg && *msg)		res->errMsg = pqResultStrdup(res, msg);	else		res->errMsg = NULL;}/* * PQclear - *	  free's the memory associated with a PGresult */voidPQclear(PGresult *res){	PGresult_data *block;	if (!res)		return;	/* Free all the subsidiary blocks */	while ((block = res->curBlock) != NULL)	{		res->curBlock = block->next;		free(block);	}	/* Free the top-level tuple pointer array */	if (res->tuples)		free(res->tuples);	/* Free the PGresult structure itself */	free(res);}/* * Handy subroutine to deallocate any partially constructed async result. */voidpqClearAsyncResult(PGconn *conn){	if (conn->result)		PQclear(conn->result);	conn->result = NULL;	conn->curTuple = NULL;}/* * addTuple *	  add a row pointer to the PGresult structure, growing it if necessary *	  Returns TRUE if OK, FALSE if not enough memory to add the row */static intaddTuple(PGresult *res, PGresAttValue *tup){	if (res->ntups >= res->tupArrSize)	{		/*		 * Try to grow the array.		 *		 * We can use realloc because shallow copying of the structure is		 * okay.  Note that the first time through, res->tuples is NULL.		 * While ANSI says that realloc() should act like malloc() in that		 * case, some old C libraries (like SunOS 4.1.x) coredump instead.		 * On failure realloc is supposed to return NULL without damaging		 * the existing allocation. Note that the positions beyond		 * res->ntups are garbage, not necessarily NULL.		 */		int			newSize = (res->tupArrSize > 0) ? res->tupArrSize * 2 : 128;		PGresAttValue **newTuples;		if (res->tuples == NULL)			newTuples = (PGresAttValue **)				malloc(newSize * sizeof(PGresAttValue *));		else			newTuples = (PGresAttValue **)				realloc(res->tuples, newSize * sizeof(PGresAttValue *));		if (!newTuples)			return FALSE;		/* malloc or realloc failed */		res->tupArrSize = newSize;		res->tuples = newTuples;	}	res->tuples[res->ntups] = tup;	res->ntups++;	return TRUE;}/* * PQsendQuery *	 Submit a query, but don't wait for it to finish * * Returns: 1 if successfully submitted *			0 if error (conn->errorMessage is set) */intPQsendQuery(PGconn *conn, const char *query){	if (!conn)		return 0;	if (!query)	{		sprintf(conn->errorMessage, "PQsendQuery() -- query pointer is null.");		return 0;	}	/* check to see if the query string is too long */	if (strlen(query) > MAX_MESSAGE_LEN - 2)	{		sprintf(conn->errorMessage, "PQsendQuery() -- query is too long.  "				"Maximum length is %d\n", MAX_MESSAGE_LEN - 2);		return 0;	}	/* Don't try to send if we know there's no live connection. */	if (conn->status != CONNECTION_OK)	{		sprintf(conn->errorMessage, "PQsendQuery() -- There is no connection "				"to the backend.\n");		return 0;	}	/* Can't send while already busy, either. */	if (conn->asyncStatus != PGASYNC_IDLE)	{		sprintf(conn->errorMessage,				"PQsendQuery() -- another query already in progress.");		return 0;	}	/* clear the error string */	conn->errorMessage[0] = '\0';	/* initialize async result-accumulation state */	conn->result = NULL;	conn->curTuple = NULL;	/* send the query to the backend; */	/* the frontend-backend protocol uses 'Q' to designate queries */	if (pqPutnchar("Q", 1, conn) ||		pqPuts(query, conn) ||		pqFlush(conn))	{		handleSendFailure(conn);		return 0;	}	/* OK, it's launched! */	conn->asyncStatus = PGASYNC_BUSY;	return 1;}/* * handleSendFailure: try to clean up after failure to send command. * * Primarily, what we want to accomplish here is to process an async * NOTICE message that the backend might have sent just before it died. * * NOTE: this routine should only be called in PGASYNC_IDLE state. */static voidhandleSendFailure(PGconn *conn){	/* Preserve the error message emitted by the failing output routine */	char * svErrMsg = strdup(conn->errorMessage);	/*	 * Accept any available input data, ignoring errors.  Note that if	 * pqReadData decides the backend has closed the channel, it will	 * close our side of the socket --- that's just what we want here.	 */	while (pqReadData(conn) > 0)		/* loop until no more data readable */ ;	/*	 * Parse any available input messages.  Since we are in PGASYNC_IDLE	 * state, only NOTICE and NOTIFY messages will be eaten.	 */	parseInput(conn);	/* Restore error message generated by output routine, if any. */	if (*svErrMsg != '\0')		strcpy(conn->errorMessage, svErrMsg);	free(svErrMsg);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -