libpq_desc_msg.patch.5

text/plain

Filename: libpq_desc_msg.patch.5
Type: text/plain
Part: 0
Message: Re: libpq Describe Extension [WAS: Bytea and perl]
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/tcop/postgres.c,v
retrieving revision 1.489
diff -c -r1.489 postgres.c
*** src/backend/tcop/postgres.c	20 Jun 2006 22:52:00 -0000	1.489
--- src/backend/tcop/postgres.c	24 Jun 2006 11:31:10 -0000
***************
*** 1853,1858 ****
--- 1853,1859 ----
  static void
  exec_describe_statement_message(const char *stmt_name)
  {
+ 	MemoryContext	oldContext;
  	PreparedStatement *pstmt;
  	TupleDesc	tupdesc;
  	ListCell   *l;
***************
*** 1865,1871 ****
  	start_xact_command();
  
  	/* Switch back to message context */
! 	MemoryContextSwitchTo(MessageContext);
  
  	/* Find prepared statement */
  	if (stmt_name[0] != '\0')
--- 1866,1872 ----
  	start_xact_command();
  
  	/* Switch back to message context */
! 	oldContext = MemoryContextSwitchTo(MessageContext);
  
  	/* Find prepared statement */
  	if (stmt_name[0] != '\0')
***************
*** 1923,1929 ****
--- 1924,1933 ----
  								  NULL);
  	else
  		pq_putemptymessage('n');	/* NoData */
+ 	
+ 	MemoryContextSwitchTo(oldContext);
  
+ 	finish_xact_command();
  }
  
  /*
***************
*** 1934,1939 ****
--- 1938,1944 ----
  static void
  exec_describe_portal_message(const char *portal_name)
  {
+ 	MemoryContext	oldContext;
  	Portal		portal;
  
  	/*
***************
*** 1943,1949 ****
  	start_xact_command();
  
  	/* Switch back to message context */
! 	MemoryContextSwitchTo(MessageContext);
  
  	portal = GetPortalByName(portal_name);
  	if (!PortalIsValid(portal))
--- 1948,1954 ----
  	start_xact_command();
  
  	/* Switch back to message context */
! 	oldContext = MemoryContextSwitchTo(MessageContext);
  
  	portal = GetPortalByName(portal_name);
  	if (!PortalIsValid(portal))
***************
*** 1975,1980 ****
--- 1980,1989 ----
  								  portal->formats);
  	else
  		pq_putemptymessage('n');	/* NoData */
+ 	
+ 	MemoryContextSwitchTo(oldContext);
+ 
+ 	finish_xact_command();
  }
  
  
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.11
diff -c -r1.11 exports.txt
*** src/interfaces/libpq/exports.txt	28 May 2006 22:42:05 -0000	1.11
--- src/interfaces/libpq/exports.txt	24 Jun 2006 11:31:10 -0000
***************
*** 130,132 ****
--- 130,134 ----
  PQencryptPassword         128
  PQisthreadsafe            129
  enlargePQExpBuffer        130
+ PQdescribePrepared        131
+ PQdescribePortal          132
Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.186
diff -c -r1.186 fe-exec.c
*** src/interfaces/libpq/fe-exec.c	28 May 2006 21:13:54 -0000	1.186
--- src/interfaces/libpq/fe-exec.c	24 Jun 2006 11:31:12 -0000
***************
*** 61,66 ****
--- 61,68 ----
  static void parseInput(PGconn *conn);
  static bool PQexecStart(PGconn *conn);
  static PGresult *PQexecFinish(PGconn *conn);
+ static int pqDescribe(PGconn *conn, const char desc_type,
+ 					  const char *desc_target);
  
  
  /* ----------------
***************
*** 2304,2309 ****
--- 2306,2415 ----
  		return 0;
  }
  
+ 
+ /*
+  * pqDescribe - Describe given prepared statement or portal.
+  *
+  * Available options for target_type are
+  *   'S' to describe a prepared statement; or
+  *   'P' to describe a portal.
+  * Returns 0 on success and 1 on failure.
+  *
+  * By issuing a PQgetResult(), response from the server will be placed
+  * in an empty PGresult which will be extractable via PQf*() function family.
+  */
+ static int
+ pqDescribe(PGconn *conn, const char desc_type, const char *desc_target)
+ {
+ 	int	ret;
+ 
+ 	/* This isn't gonna work on a 2.0 server. */
+ 	if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
+ 	{
+ 		printfPQExpBuffer(&conn->errorMessage,
+ 						  libpq_gettext("function requires at least protocol version 3.0\n"));
+ 		return 1;
+ 	}
+ 	
+ 	if (!conn)
+ 		return 1;
+ 	
+ 	/* Clear the connection error message. */
+ 	resetPQExpBuffer(&conn->errorMessage);
+ 
+ 	/* Don't try to send if we know there's no live connection. */
+ 	if (conn->status != CONNECTION_OK)
+ 	{
+ 		printfPQExpBuffer(&conn->errorMessage,
+ 						  libpq_gettext("no connection to the server\n"));
+ 		return 1;
+ 	}
+ 	
+ 	/* Can't send while already busy, either. */
+ 	if (conn->asyncStatus != PGASYNC_IDLE)
+ 	{
+ 		printfPQExpBuffer(&conn->errorMessage,
+ 						  libpq_gettext("another command is already in progress\n"));
+ 		return 1;
+ 	}
+ 
+ 	/* Initialize async result-accumulation state. */
+ 	conn->result = NULL;
+ 	conn->curTuple = NULL;
+ 	
+ 	if (desc_target)
+ 		ret = (pqPutMsgStart('D', false, conn) < 0 ||
+ 			   pqPutc(desc_type, conn) < 0 ||
+ 			   pqPuts(desc_target, conn) < 0 ||
+ 			   pqPutMsgEnd(conn) < 0);
+ 	
+ 	/* NULL desc_target values will be treated as an empty string. */
+ 	else
+ 		ret = (pqPutMsgStart('D', false, conn) < 0 ||
+ 			   pqPutc(desc_type, conn) < 0 ||
+ 			   pqPutc('\0', conn) < 0 ||
+ 			   pqPutMsgEnd(conn) < 0);
+ 	
+ 	if (ret)
+ 		goto SendFailure;
+ 	
+ 	/* Extended protocol requires a Sync message. */
+ 	if (pqPutMsgStart('S', false, conn) < 0 ||
+ 		pqPutMsgEnd(conn) < 0)
+ 		goto SendFailure;
+ 
+ 	/* Remember we'll parse a Describe response. */
+ 	conn->queryclass = PGQUERY_DESCRIBE;
+ 
+ 	/* Free last query string. */
+ 	if (conn->last_query)
+ 	{
+ 		free(conn->last_query);
+ 		conn->last_query = NULL;
+ 	}
+ 
+ 	/* Describe request is sent. */
+ 	conn->asyncStatus = PGASYNC_BUSY;
+ 	return 0;
+ 
+ SendFailure:
+ 	pqHandleSendFailure(conn);
+ 	return 1;
+ }
+ 
+ int
+ PQdescribePrepared(PGconn *conn, const char *stmt)
+ {
+ 	return pqDescribe(conn, 'S', stmt);
+ }
+ 
+ int
+ PQdescribePortal(PGconn *conn, const char *portal)
+ {
+ 	return pqDescribe(conn, 'P', portal);
+ }
+ 
+ 
  /* PQsetnonblocking:
   *	sets the PGconn's database connection non-blocking if the arg is TRUE
   *	or makes it non-blocking if the arg is FALSE, this will not protect
***************
*** 2381,2386 ****
--- 2487,2493 ----
  	free(ptr);
  }
  
+ 
  /*
   * PQfreeNotify - free's the memory associated with a PGnotify
   *
Index: src/interfaces/libpq/fe-protocol3.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v
retrieving revision 1.26
diff -c -r1.26 fe-protocol3.c
*** src/interfaces/libpq/fe-protocol3.c	14 Mar 2006 22:48:23 -0000	1.26
--- src/interfaces/libpq/fe-protocol3.c	24 Jun 2006 11:31:13 -0000
***************
*** 45,50 ****
--- 45,51 ----
  
  static void handleSyncLoss(PGconn *conn, char id, int msgLength);
  static int	getRowDescriptions(PGconn *conn);
+ static int	getParamDescriptions(PGconn *conn);
  static int	getAnotherTuple(PGconn *conn, int msgLength);
  static int	getParameterStatus(PGconn *conn);
  static int	getNotify(PGconn *conn);
***************
*** 81,87 ****
  		if (pqGetc(&id, conn))
  			return;
  		if (pqGetInt(&msgLength, 4, conn))
! 			return;
  
  		/*
  		 * Try to validate message type/length here.  A length less than 4 is
--- 82,88 ----
  		if (pqGetc(&id, conn))
  			return;
  		if (pqGetInt(&msgLength, 4, conn))
! 			return;	
  
  		/*
  		 * Try to validate message type/length here.  A length less than 4 is
***************
*** 206,211 ****
--- 207,213 ----
  					}
  					strncpy(conn->result->cmdStatus, conn->workBuffer.data,
  							CMDSTATUS_LEN);
+ 
  					conn->asyncStatus = PGASYNC_READY;
  					break;
  				case 'E':		/* error return */
***************
*** 216,221 ****
--- 218,236 ----
  				case 'Z':		/* backend is ready for new query */
  					if (getReadyForQuery(conn))
  						return;
+ 					
+ 					/* 
+ 					 * If there's any previosly available result and this is
+ 					 * received from a Describe query's response, consume it
+ 					 * first.
+ 					 */
+ 					if (conn->result && conn->queryclass == PGQUERY_DESCRIBE)
+ 					{
+ 						conn->asyncStatus = PGASYNC_READY;
+ 						return;
+ 					}
+ 					
+ 					/* Backend is ready for a new query. */
  					conn->asyncStatus = PGASYNC_IDLE;
  					break;
  				case 'I':		/* empty query */
***************
*** 294,299 ****
--- 309,338 ----
  						conn->result = PQmakeEmptyPGresult(conn,
  														   PGRES_COMMAND_OK);
  					break;
+ 				case 't':		/* Parameter Description */
+ 					/* Parse after a pqDescribe call only. */
+ 					if (conn->queryclass == PGQUERY_DESCRIBE)
+ 					{
+ 						if (!conn->result)
+ 						{
+ 							/* A new PGresult will be created to load parsed data into. */
+ 							if (getParamDescriptions(conn))
+ 								return;
+ 							
+ 							/* Result is ready. */
+ 							conn->asyncStatus = PGASYNC_READY;
+ 						}
+ 						else
+ 						{
+ 							/*
+ 							 * We'll wait until the application accepts the current
+ 							 * existing result.
+ 							 */
+ 							conn->asyncStatus = PGASYNC_READY;
+ 							return;
+ 						}
+ 					}
+ 					break;
  				case 'D':		/* Data Row */
  					if (conn->result != NULL &&
  						conn->result->resultStatus == PGRES_TUPLES_OK)
***************
*** 500,505 ****
--- 539,611 ----
  }
  
  /*
+  * parseInput subroutine to read a 't' (ParameterDescription) message.
+  * Returns 0 if parsing is completed, 1 if there isn't enough data yet
+  * and EOF if an error occurs.
+  *
+  * Parsed data will get loaded into the existing conn->result.
+  */
+ static int
+ getParamDescriptions(PGconn *conn)
+ {
+ 	PGresult	*res;
+ 	int			 nattr;
+ 	int			 i;
+ 	Oid			 typid;
+ 	
+ 	res = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK);
+ 	if (!res)
+ 	{
+ 		printfPQExpBuffer(&conn->errorMessage,
+ 						  libpq_gettext("out of memory\n"));
+ 		goto Error;
+ 	}
+ 
+ 	/* First extracting number of attributes from the message. */
+ 	if (pqGetInt(&nattr, 2, conn) < 0)
+ 		goto NotEnoughData;
+ 	res->numAttributes = nattr;
+ 
+ 	/* Allocate space for the attribute descriptors. */		
+ 	if (nattr > 0)
+ 	{
+ 		int sz = nattr * sizeof(PGresAttDesc);
+ 		
+ 		res->attDescs = (PGresAttDesc *) pqResultAlloc(res, sz, TRUE);
+ 		if (!res->attDescs)
+ 		{
+ 			printfPQExpBuffer(&conn->errorMessage,
+ 							  libpq_gettext("out of memory\n"));
+ 			goto Error;
+ 		}
+ 		
+ 		/* Reset attribute values. */
+ 		MemSet(res->attDescs, 0, sz);
+ 	}
+ 
+ 	/* Loop through attribute's type OIDs. */
+ 	for (i = 0; i < nattr; i++)
+ 	{
+ 		if (pqGetInt(&typid, 4, conn) < 0)
+ 			goto NotEnoughData;
+ 		res->attDescs[i].typid = typid;
+ 	}
+ 
+ 	/* Success. */
+ 	conn->result = res;
+ 	return 0;
+ 
+ NotEnoughData:
+ 	PQclear(res);
+ 	return 1;
+ 
+ Error:
+ 	if (res)
+ 		PQclear(res);
+ 	return EOF;
+ }
+ 
+ /*
   * parseInput subroutine to read a 'D' (row data) message.
   * We add another tuple to the existing PGresult structure.
   * Returns: 0 if completed message, EOF if error or not enough data yet.
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.129
diff -c -r1.129 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h	23 May 2006 22:13:19 -0000	1.129
--- src/interfaces/libpq/libpq-fe.h	24 Jun 2006 11:31:14 -0000
***************
*** 407,412 ****
--- 407,416 ----
  extern int	PQgetlength(const PGresult *res, int tup_num, int field_num);
  extern int	PQgetisnull(const PGresult *res, int tup_num, int field_num);
  
+ /* Describe prepared statements and portals. */
+ extern int	PQdescribePrepared(PGconn *conn, const char *stmt);
+ extern int	PQdescribePortal(PGconn *conn, const char *portal);
+ 
  /* Delete a PGresult */
  extern void PQclear(PGresult *res);
  
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.113
diff -c -r1.113 libpq-int.h
*** src/interfaces/libpq/libpq-int.h	21 May 2006 20:19:23 -0000	1.113
--- src/interfaces/libpq/libpq-int.h	24 Jun 2006 11:31:14 -0000
***************
*** 193,199 ****
  {
  	PGQUERY_SIMPLE,				/* simple Query protocol (PQexec) */
  	PGQUERY_EXTENDED,			/* full Extended protocol (PQexecParams) */
! 	PGQUERY_PREPARE				/* Parse only (PQprepare) */
  } PGQueryClass;
  
  /* PGSetenvStatusType defines the state of the PQSetenv state machine */
--- 193,200 ----
  {
  	PGQUERY_SIMPLE,				/* simple Query protocol (PQexec) */
  	PGQUERY_EXTENDED,			/* full Extended protocol (PQexecParams) */
! 	PGQUERY_PREPARE,			/* Parse only (PQprepare) */
! 	PGQUERY_DESCRIBE			/* Parse only (pqDescribe) */
  } PGQueryClass;
  
  /* PGSetenvStatusType defines the state of the PQSetenv state machine */