77 *
88 *
99 * IDENTIFICATION
10- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.25 1996/12/28 01:57:13 momjian Exp $
10+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.26 1996/12/31 07:29:15 bryanh Exp $
1111 *
1212 *-------------------------------------------------------------------------
1313 */
@@ -348,6 +348,170 @@ makePGresult(PGconn* conn, char* pname)
348348}
349349
350350
351+ /*
352+ * Assuming that we just sent a query to the backend, read the backend's
353+ * response from stream <pfin> and respond accordingly.
354+ *
355+ * If <pfdebug> is non-null, write to that stream whatever we receive
356+ * (it's a debugging trace).
357+ *
358+ * Return as <result> a pointer to a proper final PGresult structure,
359+ * newly allocated, for the query based on the response we get. If the
360+ * response we get indicates that the query didn't execute, return a
361+ * null pointer and don't allocate any space, but also place a text
362+ * string explaining the problem at <*reason>.
363+ */
364+
365+ static void
366+ process_response_from_backend (FILE * pfin ,FILE * pfout ,FILE * pfdebug ,
367+ PGconn * conn ,
368+ PGresult * * result_p ,char * const reason ) {
369+
370+ char id ;
371+ /* The protocol character received from the backend. The protocol
372+ character is the first character in the backend's response to our
373+ query. It defines the nature of the response.
374+ */
375+ PGnotify * newNotify ;
376+ bool done ;
377+ /* We're all done with the query and ready to return the result. */
378+ int emptiesSent ;
379+ /* Number of empty queries we have sent in order to flush out multiple
380+ responses, less the number of corresponding responses we have
381+ received.
382+ */
383+ char cmdStatus [MAX_MESSAGE_LEN ];
384+ char pname [MAX_MESSAGE_LEN ];/* portal name */
385+
386+ /* loop because multiple messages, especially NOTICES,
387+ can come back from the backend. NOTICES are output directly to stderr
388+ */
389+
390+ emptiesSent = 0 ;/* No empty queries sent yet */
391+ pname [0 ]= '\0' ;
392+
393+ done = false;/* initial value */
394+ while (!done ) {
395+ /* read the result id */
396+ id = pqGetc (pfin ,pfdebug );
397+ if (id == EOF ) {
398+ /* hmm, no response from the backend-end, that's bad */
399+ (void )sprintf (reason ,
400+ "PQexec() -- Request was sent to backend, but backend "
401+ "closed the channel before "
402+ "responding. This probably means the backend "
403+ "terminated abnormally before or while processing "
404+ "the request.\n" );
405+ conn -> status = CONNECTION_BAD ;/* No more connection to backend */
406+ * result_p = (PGresult * )NULL ;
407+ done = true;
408+ }else {
409+ switch (id ) {
410+ case 'A' :
411+ newNotify = (PGnotify * )malloc (sizeof (PGnotify ));
412+ pqGetInt (& (newNotify -> be_pid ),4 ,pfin ,pfdebug );
413+ pqGets (newNotify -> relname ,NAMEDATALEN ,pfin ,pfdebug );
414+ DLAddTail (conn -> notifyList ,DLNewElem (newNotify ));
415+ /* async messages are piggy'ed back on other messages,
416+ so we stay in the while loop for other messages */
417+ break ;
418+ case 'C' :/* portal query command, no rows returned */
419+ if (pqGets (cmdStatus ,MAX_MESSAGE_LEN ,pfin ,pfdebug )== 1 ) {
420+ sprintf (reason ,
421+ "PQexec() -- query command completed, "
422+ "but return message from backend cannot be read." );
423+ * result_p = (PGresult * )NULL ;
424+ done = true;
425+ }else {
426+ /*
427+ // since backend may produce more than one result for some
428+ // commands need to poll until clear
429+ // send an empty query down, and keep reading out of the pipe
430+ // until an 'I' is received.
431+ */
432+ pqPuts ("Q " ,pfout ,pfdebug );/* send an empty query */
433+ /*
434+ * Increment a flag and process messages in the usual way because
435+ * there may be async notifications pending. DZ - 31-8-1996
436+ */
437+ emptiesSent ++ ;
438+ }
439+ break ;
440+ case 'E' :/* error return */
441+ if (pqGets (conn -> errorMessage ,ERROR_MSG_LENGTH ,pfin ,pfdebug )== 1 ) {
442+ (void )sprintf (reason ,
443+ "PQexec() -- error return detected from backend, "
444+ "but attempt to read the error message failed." );
445+ }
446+ * result_p = (PGresult * )NULL ;
447+ done = true;
448+ break ;
449+ case 'I' : {/* empty query */
450+ /* read and throw away the closing '\0' */
451+ int c ;
452+ if ((c = pqGetc (pfin ,pfdebug ))!= '\0' ) {
453+ fprintf (stderr ,"error!, unexpected character %c following 'I'\n" ,c );
454+ }
455+ if (emptiesSent ) {
456+ if (-- emptiesSent == 0 ) {/* is this the last one? */
457+ /*
458+ * If this is the result of a portal query command set the
459+ * command status and message accordingly. DZ - 31-8-1996
460+ */
461+ * result_p = makeEmptyPGresult (conn ,PGRES_COMMAND_OK );
462+ strncpy ((* result_p )-> cmdStatus ,cmdStatus ,CMDSTATUS_LEN - 1 );
463+ done = true;
464+ }
465+ }
466+ else {
467+ * result_p = makeEmptyPGresult (conn ,PGRES_EMPTY_QUERY );
468+ done = true;
469+ }
470+ }
471+ break ;
472+ case 'N' :/* notices from the backend */
473+ if (pqGets (reason ,ERROR_MSG_LENGTH ,pfin ,pfdebug )== 1 ) {
474+ sprintf (reason ,
475+ "PQexec() -- Notice detected from backend, "
476+ "but attempt to read the notice failed." );
477+ * result_p = (PGresult * )NULL ;
478+ done = true;
479+ }else
480+ /* Should we really be doing this? These notices are not important
481+ enough for us to presume to put them on stderr. Maybe the caller
482+ should decide whether to put them on stderr or not. BJH 96.12.27
483+ */
484+ fprintf (stderr ,"%s" ,reason );
485+ break ;
486+ case 'P' :/* synchronous (normal) portal */
487+ pqGets (pname ,MAX_MESSAGE_LEN ,pfin ,pfdebug );/* read in portal name*/
488+ break ;
489+ case 'T' :/* actual row results: */
490+ * result_p = makePGresult (conn ,pname );
491+ done = true;
492+ break ;
493+ case 'D' :/* copy command began successfully */
494+ * result_p = makeEmptyPGresult (conn ,PGRES_COPY_IN );
495+ done = true;
496+ break ;
497+ case 'B' :/* copy command began successfully */
498+ * result_p = makeEmptyPGresult (conn ,PGRES_COPY_OUT );
499+ done = true;
500+ break ;
501+ default :
502+ sprintf (reason ,
503+ "unknown protocol character '%c' read from backend. "
504+ "(The protocol character is the first character the "
505+ "backend sends in response to a query it receives).\n" ,
506+ id );
507+ * result_p = (PGresult * )NULL ;
508+ done = true;
509+ }/* switch on protocol character */
510+ }/* if character was received */
511+ }/* while not done */
512+ }
513+
514+
351515
352516/*
353517 * PQexec
@@ -364,27 +528,14 @@ PGresult*
364528PQexec (PGconn * conn ,const char * query )
365529{
366530PGresult * result ;
367- int id ;
368531char buffer [MAX_MESSAGE_LEN ];
369- char cmdStatus [MAX_MESSAGE_LEN ];
370- char pname [MAX_MESSAGE_LEN ];/* portal name */
371- PGnotify * newNotify ;
372- FILE * pfin ,* pfout ,* pfdebug ;
373- static int emptiesPending = 0 ;
374- bool emptySent = false;
375-
376- pname [0 ]= '\0' ;
377532
378533if (!conn )return NULL ;
379534if (!query ) {
380535sprintf (conn -> errorMessage ,"PQexec() -- query pointer is null." );
381536return NULL ;
382537 }
383538
384- pfin = conn -> Pfin ;
385- pfout = conn -> Pfout ;
386- pfdebug = conn -> Pfdebug ;
387-
388539/*clear the error string */
389540conn -> errorMessage [0 ]= '\0' ;
390541
@@ -406,129 +557,20 @@ PQexec(PGconn* conn, const char* query)
406557sprintf (buffer ,"Q%s" ,query );
407558
408559/* send the query to the backend; */
409- if (pqPuts (buffer ,pfout , pfdebug )== 1 ) {
560+ if (pqPuts (buffer ,conn -> Pfout , conn -> Pfdebug )== 1 ) {
410561 (void )sprintf (conn -> errorMessage ,
411562"PQexec() -- while sending query: %s\n"
412563"-- fprintf to Pfout failed: errno=%d\n%s\n" ,
413- query ,errno ,strerror (errno ));
564+ query ,errno ,strerror (errno ));
414565return NULL ;
415566 }
416567
417- /* loop forever because multiple messages, especially NOTICES,
418- can come back from the backend
419- NOTICES are output directly to stderr
420- */
421-
422- while (1 ) {
568+ process_response_from_backend (conn -> Pfin ,conn -> Pfout ,conn -> Pfdebug ,conn ,
569+ & result ,conn -> errorMessage );
570+ return (result );
571+ }
423572
424- /* read the result id */
425- id = pqGetc (pfin ,pfdebug );
426- if (id == EOF ) {
427- /* hmm, no response from the backend-end, that's bad */
428- (void )sprintf (conn -> errorMessage ,
429- "PQexec() -- Request was sent to backend, but backend "
430- "closed the channel before "
431- "responding. This probably means the backend "
432- "terminated abnormally before or while processing "
433- "the request.\n" );
434- conn -> status = CONNECTION_BAD ;/* No more connection to backend */
435- return (PGresult * )NULL ;
436- }
437573
438- switch (id ) {
439- case 'A' :
440- newNotify = (PGnotify * )malloc (sizeof (PGnotify ));
441- pqGetInt (& (newNotify -> be_pid ),4 ,pfin ,pfdebug );
442- pqGets (newNotify -> relname ,NAMEDATALEN ,pfin ,pfdebug );
443- DLAddTail (conn -> notifyList ,DLNewElem (newNotify ));
444- /* async messages are piggy'ed back on other messages,
445- so we stay in the while loop for other messages */
446- break ;
447- case 'C' :/* portal query command, no rows returned */
448- if (pqGets (cmdStatus ,MAX_MESSAGE_LEN ,pfin ,pfdebug )== 1 ) {
449- sprintf (conn -> errorMessage ,
450- "PQexec() -- query command completed, "
451- "but return message from backend cannot be read." );
452- return (PGresult * )NULL ;
453- }
454- else {
455- /*
456- // since backend may produce more than one result for some commands
457- // need to poll until clear
458- // send an empty query down, and keep reading out of the pipe
459- // until an 'I' is received.
460- */
461- pqPuts ("Q" ,pfout ,pfdebug );/* send an empty query */
462- /*
463- * Increment a flag and process messages in the usual way because
464- * there may be async notifications pending. DZ - 31-8-1996
465- */
466- emptiesPending ++ ;
467- emptySent = true;
468- }
469- break ;
470- case 'E' :/* error return */
471- if (pqGets (conn -> errorMessage ,ERROR_MSG_LENGTH ,pfin ,pfdebug )== 1 ) {
472- (void )sprintf (conn -> errorMessage ,
473- "PQexec() -- error return detected from backend, "
474- "but attempt to read the error message failed." );
475- }
476- return (PGresult * )NULL ;
477- break ;
478- case 'I' :/* empty query */
479- /* read the throw away the closing '\0' */
480- {
481- int c ;
482- if ((c = pqGetc (pfin ,pfdebug ))!= '\0' ) {
483- fprintf (stderr ,"error!, unexpected character %c following 'I'\n" ,c );
484- }
485- if (emptiesPending ) {
486- if (-- emptiesPending == 0 && emptySent ) {/* is this the last one? */
487- /*
488- * If this is the result of a portal query command set the
489- * command status and message accordingly. DZ - 31-8-1996
490- */
491- result = makeEmptyPGresult (conn ,PGRES_COMMAND_OK );
492- strncpy (result -> cmdStatus ,cmdStatus ,CMDSTATUS_LEN - 1 );
493- return result ;
494- }
495- }
496- else {
497- result = makeEmptyPGresult (conn ,PGRES_EMPTY_QUERY );
498- return result ;
499- }
500- }
501- break ;
502- case 'N' :/* notices from the backend */
503- if (pqGets (conn -> errorMessage ,ERROR_MSG_LENGTH ,pfin ,pfdebug )== 1 ) {
504- sprintf (conn -> errorMessage ,
505- "PQexec() -- Notice detected from backend, "
506- "but attempt to read the notice failed." );
507- return (PGresult * )NULL ;
508- }
509- else
510- fprintf (stderr ,"%s" ,conn -> errorMessage );
511- break ;
512- case 'P' :/* synchronous (normal) portal */
513- pqGets (pname ,MAX_MESSAGE_LEN ,pfin ,pfdebug );/* read in portal name*/
514- break ;
515- case 'T' :/* actual row results: */
516- return makePGresult (conn ,pname );
517- break ;
518- case 'D' :/* copy command began successfully */
519- return makeEmptyPGresult (conn ,PGRES_COPY_IN );
520- break ;
521- case 'B' :/* copy command began successfully */
522- return makeEmptyPGresult (conn ,PGRES_COPY_OUT );
523- break ;
524- default :
525- sprintf (conn -> errorMessage ,
526- "unknown protocol character %c read from backend\n" ,
527- id );
528- return (PGresult * )NULL ;
529- }/* switch */
530- }/* while (1)*/
531- }
532574
533575/*
534576 * PQnotifies