fe-exec.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,443 行 · 第 1/4 页

C
2,443
字号
				/* keep waiting to swallow the copy's failure message */			}			else			{				/* In older protocols we have to punt */				printfPQExpBuffer(&conn->errorMessage,								  libpq_gettext("COPY IN state must be terminated first\n"));				return false;			}		}		else if (resultStatus == PGRES_COPY_OUT)		{			if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)			{				/*				 * In protocol 3, we can get out of a COPY OUT state: we				 * just switch back to BUSY and allow the remaining COPY				 * data to be dropped on the floor.				 */				conn->asyncStatus = PGASYNC_BUSY;				/* keep waiting to swallow the copy's completion message */			}			else			{				/* In older protocols we have to punt */				printfPQExpBuffer(&conn->errorMessage,								  libpq_gettext("COPY OUT state must be terminated first\n"));				return false;			}		}		/* check for loss of connection, too */		if (conn->status == CONNECTION_BAD)			return false;	}	/* OK to send a command */	return true;}/* * Common code for PQexec and sibling routines: wait for command result */static PGresult *PQexecFinish(PGconn *conn){	PGresult   *result;	PGresult   *lastResult;	/*	 * For backwards compatibility, return the last result if there are	 * more than one --- but merge error messages if we get more than one	 * error result.	 *	 * We have to stop if we see copy in/out, however. We will resume parsing	 * after application performs the data transfer.	 *	 * Also stop if the connection is lost (else we'll loop infinitely).	 */	lastResult = NULL;	while ((result = PQgetResult(conn)) != NULL)	{		if (lastResult)		{			if (lastResult->resultStatus == PGRES_FATAL_ERROR &&				result->resultStatus == PGRES_FATAL_ERROR)			{				pqCatenateResultError(lastResult, result->errMsg);				PQclear(result);				result = lastResult;				/*				 * Make sure PQerrorMessage agrees with concatenated				 * result				 */				resetPQExpBuffer(&conn->errorMessage);				appendPQExpBufferStr(&conn->errorMessage, result->errMsg);			}			else				PQclear(lastResult);		}		lastResult = result;		if (result->resultStatus == PGRES_COPY_IN ||			result->resultStatus == PGRES_COPY_OUT ||			conn->status == CONNECTION_BAD)			break;	}	return lastResult;}/* * PQnotifies *	  returns a PGnotify* structure of the latest async notification * that has not yet been handled * * returns NULL, if there is currently * no unhandled async notification from the backend * * the CALLER is responsible for FREE'ing the structure returned */PGnotify *PQnotifies(PGconn *conn){	Dlelem	   *e;	PGnotify   *event;	if (!conn)		return NULL;	/* Parse any available data to see if we can extract NOTIFY messages. */	parseInput(conn);	/* RemHead returns NULL if list is empty */	e = DLRemHead(conn->notifyList);	if (!e)		return NULL;	event = (PGnotify *) DLE_VAL(e);	DLFreeElem(e);	return event;}/* * PQputCopyData - send some data to the backend during COPY IN * * Returns 1 if successful, 0 if data could not be sent (only possible * in nonblock mode), or -1 if an error occurs. */intPQputCopyData(PGconn *conn, const char *buffer, int nbytes){	if (!conn)		return -1;	if (conn->asyncStatus != PGASYNC_COPY_IN)	{		printfPQExpBuffer(&conn->errorMessage,						  libpq_gettext("no COPY in progress\n"));		return -1;	}	/*	 * Check for NOTICE messages coming back from the server.  Since the	 * server might generate multiple notices during the COPY, we have to	 * consume those in a reasonably prompt fashion to prevent the comm	 * buffers from filling up and possibly blocking the server.	 */	if (!PQconsumeInput(conn))		return -1;				/* I/O failure */	parseInput(conn);	if (nbytes > 0)	{		/*		 * Try to flush any previously sent data in preference to growing		 * the output buffer.  If we can't enlarge the buffer enough to		 * hold the data, return 0 in the nonblock case, else hard error.		 * (For simplicity, always assume 5 bytes of overhead even in		 * protocol 2.0 case.)		 */		if ((conn->outBufSize - conn->outCount - 5) < nbytes)		{			if (pqFlush(conn) < 0)				return -1;			if (pqCheckOutBufferSpace(conn->outCount + 5 + nbytes, conn))				return pqIsnonblocking(conn) ? 0 : -1;		}		/* Send the data (too simple to delegate to fe-protocol files) */		if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)		{			if (pqPutMsgStart('d', false, conn) < 0 ||				pqPutnchar(buffer, nbytes, conn) < 0 ||				pqPutMsgEnd(conn) < 0)				return -1;		}		else		{			if (pqPutMsgStart(0, false, conn) < 0 ||				pqPutnchar(buffer, nbytes, conn) < 0 ||				pqPutMsgEnd(conn) < 0)				return -1;		}	}	return 1;}/* * PQputCopyEnd - send EOF indication to the backend during COPY IN * * After calling this, use PQgetResult() to check command completion status. * * Returns 1 if successful, 0 if data could not be sent (only possible * in nonblock mode), or -1 if an error occurs. */intPQputCopyEnd(PGconn *conn, const char *errormsg){	if (!conn)		return -1;	if (conn->asyncStatus != PGASYNC_COPY_IN)	{		printfPQExpBuffer(&conn->errorMessage,						  libpq_gettext("no COPY in progress\n"));		return -1;	}	/*	 * Send the COPY END indicator.  This is simple enough that we don't	 * bother delegating it to the fe-protocol files.	 */	if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)	{		if (errormsg)		{			/* Send COPY FAIL */			if (pqPutMsgStart('f', false, conn) < 0 ||				pqPuts(errormsg, conn) < 0 ||				pqPutMsgEnd(conn) < 0)				return -1;		}		else		{			/* Send COPY DONE */			if (pqPutMsgStart('c', false, conn) < 0 ||				pqPutMsgEnd(conn) < 0)				return -1;		}		/*		 * If we sent the COPY command in extended-query mode, we must		 * issue a Sync as well.		 */		if (conn->ext_query)		{			if (pqPutMsgStart('S', false, conn) < 0 ||				pqPutMsgEnd(conn) < 0)				return -1;		}	}	else	{		if (errormsg)		{			/* Ooops, no way to do this in 2.0 */			printfPQExpBuffer(&conn->errorMessage,			 libpq_gettext("function requires at least protocol version 3.0\n"));			return -1;		}		else		{			/* Send old-style end-of-data marker */			if (pqPutMsgStart(0, false, conn) < 0 ||				pqPutnchar("\\.\n", 3, conn) < 0 ||				pqPutMsgEnd(conn) < 0)				return -1;		}	}	/* Return to active duty */	conn->asyncStatus = PGASYNC_BUSY;	resetPQExpBuffer(&conn->errorMessage);	/* Try to flush data */	if (pqFlush(conn) < 0)		return -1;	return 1;}/* * PQgetCopyData - read a row of data from the backend during COPY OUT * * If successful, sets *buffer to point to a malloc'd row of data, and * returns row length (always > 0) as result. * Returns 0 if no row available yet (only possible if async is true), * -1 if end of copy (consult PQgetResult), or -2 if error (consult * PQerrorMessage). */intPQgetCopyData(PGconn *conn, char **buffer, int async){	*buffer = NULL;				/* for all failure cases */	if (!conn)		return -2;	if (conn->asyncStatus != PGASYNC_COPY_OUT)	{		printfPQExpBuffer(&conn->errorMessage,						  libpq_gettext("no COPY in progress\n"));		return -2;	}	if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)		return pqGetCopyData3(conn, buffer, async);	else		return pqGetCopyData2(conn, buffer, async);}/* * PQgetline - gets a newline-terminated string from the backend. * * Chiefly here so that applications can use "COPY <rel> to stdout" * and read the output string.	Returns a null-terminated string in s. * * XXX this routine is now deprecated, because it can't handle binary data. * If called during a COPY BINARY we return EOF. * * PQgetline reads up to maxlen-1 characters (like fgets(3)) but strips * the terminating \n (like gets(3)). * * CAUTION: the caller is responsible for detecting the end-of-copy signal * (a line containing just "\.") when using this routine. * * RETURNS: *		EOF if error (eg, invalid arguments are given) *		0 if EOL is reached (i.e., \n has been read) *				(this is required for backward-compatibility -- this *				 routine used to always return EOF or 0, assuming that *				 the line ended within maxlen bytes.) *		1 in other cases (i.e., the buffer was filled before \n is reached) */intPQgetline(PGconn *conn, char *s, int maxlen){	if (!s || maxlen <= 0)		return EOF;	*s = '\0';	/* maxlen must be at least 3 to hold the \. terminator! */	if (maxlen < 3)		return EOF;	if (!conn)		return EOF;	if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)		return pqGetline3(conn, s, maxlen);	else		return pqGetline2(conn, s, maxlen);}/* * PQgetlineAsync - gets a COPY data row without blocking. * * This routine is for applications that want to do "COPY <rel> to stdout" * asynchronously, that is without blocking.  Having issued the COPY command * and gotten a PGRES_COPY_OUT response, the app should call PQconsumeInput * and this routine until the end-of-data signal is detected.  Unlike * PQgetline, this routine takes responsibility for detecting end-of-data. * * On each call, PQgetlineAsync will return data if a complete data row * is available in libpq's input buffer.  Otherwise, no data is returned * until the rest of the row arrives. * * If -1 is returned, the end-of-data signal has been recognized (and removed * from libpq's input buffer).  The caller *must* next call PQendcopy and * then return to normal processing. * * RETURNS: *	 -1    if the end-of-copy-data marker has been recognized *	 0	   if no data is available *	 >0    the number of bytes returned. * * The data returned will not extend beyond a data-row boundary.  If possible * a whole row will be returned at one time.  But if the buffer offered by * the caller is too small to hold a row sent by the backend, then a partial * data row will be returned.  In text mode this can be detected by testing * whether the last returned byte is '\n' or not. * * The returned data is *not* null-terminated. */intPQgetlineAsync(PGconn *conn, char *buffer, int bufsize){	if (!conn)		return -1;	if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)		return pqGetlineAsync3(conn, buffer, bufsize);	else		return pqGetlineAsync2(conn, buffer, bufsize);}/* * PQputline -- sends a string to the backend during COPY IN. * Returns 0 if OK, EOF if not. * * This is deprecated primarily because the return convention doesn't allow * caller to tell the difference between a hard error and a nonblock-mode * send failure. */intPQputline(PGconn *conn, const char *s){	return PQputnbytes(conn, s, strlen(s));}/* * PQputnbytes -- like PQputline, but buffer need not be null-terminated. * Returns 0 if OK, EOF if not. */intPQputnbytes(PGconn *conn, const char *buffer, int nbytes){	if (PQputCopyData(conn, buffer, nbytes) > 0)		return 0;	else		return EOF;}/* * PQendcopy *		After completing the data transfer portion of a copy in/out, *		the application must call this routine to finish the command protocol. * * When using protocol 3.0 this is deprecated; it's cleaner to use PQgetResult * to get the transfer status.	Note however that when using 2.0 protocol, * recovering from a copy failure often requires a PQreset.  PQendcopy will * take care of that, PQgetResult won't. * * RETURNS: *		0 on success *		1 on failure */intPQendcopy(PGconn *conn){	if (!conn)		return 0;	if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)		return pqEndcopy3(conn);	else		return pqEndcopy2(conn);}/* ---------------- *		PQfn -	Send a function call to the POSTGRES backend. * *		conn			: backend connection *		fnid			: function id *		result_buf		: pointer to result buffer (&int if integer) *		result_len		: length of return value. *		actual_result_len: actual length returned. (differs from result_len *						  for varlena structures.) *		result_type		: If the result is an integer, this must be 1, *						  otherwise this should be 0 *		args			: pointer to an array of function arguments. *						  (each has length, if integer, and value/pointer) *		nargs			: # of arguments in args array. * * RETURNS *		PGresult with status = PGRES_COMMAND_OK if successful. *			*actual_result_len is > 0 if there is a return value, 0 if not. *		PGresult with status = PGRES_FATAL_ERROR if backend returns an error. *		NULL on communications failure.  conn->errorMessage will be set. * ---------------- */PGresult *PQfn(PGconn *conn,	 int fnid,	 int *result_buf,	 int *actual_result_len,	 int result_is_int,	 const PQArgBlock *args,	 int nargs){	*actual_result_len = 0;	if (!conn)		return NULL;	/* clear the error string */	resetPQExpBuffer(&conn->errorMessage);	if (conn->sock < 0 || conn->asyncStatus != PGASYNC_IDLE ||		conn->result != NULL)	{		printfPQExpBuffer(&conn->errorMessage,						  libpq_gettext("connection in wrong state\n"));		return NULL;	}	if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)		return pqFunctionCall3(conn, fnid,							   result_buf, actual_result_len,							   result_is_int,							   args, nargs);	else		return pqFunctionCall2(conn, fnid,							   result_buf, actual_result_len,							   result_is_int,							   args, nargs);}/* ====== accessor funcs for PGresult ======== */ExecStatusTypePQresultStatus(const PGresult *res){	if (!res)		return PGRES_FATAL_ERROR;	return res->resultStatus;}char *PQresStatus(ExecStatusType status){	if (status < 0 || status >= sizeof pgresStatus / sizeof pgresStatus[0])		return libpq_gettext("invalid ExecStatusType code");	return pgresStatus[status];}char *PQresultErrorMessage(const PGresult *res){	if (!res || !res->errMsg)		return "";	return res->errMsg;}char *PQresultErrorField(const PGresult *res, int fieldcode){	PGMessageField *pfield;	if (!res)		return NULL;	for (pfield = res->errFields; pfield != NULL; pfield = pfield->next)	{		if (pfield->code == fieldcode)			return pfield->contents;	}	return NULL;}intPQntuples(const PGresult *res){	if (!res)		return 0;	return res->ntups;}intPQnfields(const PGresult *res){	if (!res)		return 0;	return res->numAttributes;}intPQbinaryTuples(const PGresult *res){	if (!res)		return 0;	return res->binary;}/* * Helper routines to range-check field numbers and tuple numbers. * Return TRUE if OK, FALSE if not */static intcheck_field_number(const PGresult *res, int field_num){	if (!res)		return FALSE;			/* no way to display error message... */	if (field_num < 0 || field_num >= res->numAttributes)	{		pqInternalNotice(&res->noticeHooks,						 "column number %d is out of range 0..%d",						 field_num, res->numAttributes - 1);		return FALSE;	}	return TRUE;}static intcheck_tuple_field_number(const PGresult *res,						 int tup_num, int field_num){	if (!res)		return FALSE;			/* no way to display error message... */	if (tup_num < 0 || tup_num >= res->ntups)	{		pqInternalNotice(&res->noticeHooks,						 "row number %d is out of range 0..%d",						 tup_num, res->ntups - 1);		return FALSE;	}	if (field_num < 0 || field_num >= res->numAttributes)	{		pqInternalNotice(&res->noticeHooks,						 "column number %d is out of range 0..%d",						 field_num, res->numAttributes - 1);		return FALSE;	}	return TRUE;}/* * returns NULL if the field_num is invalid */char *PQfname(const PGresult *res, int field_num){	if (!check_field_number(res, field_num))		return NULL;	if (res->attDescs)		return res->attDescs[field_num].name;

⌨️ 快捷键说明

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