@@ -438,7 +438,7 @@ ResetCancelConn(void)
438
438
static bool
439
439
AcceptResult (const PGresult * result )
440
440
{
441
- bool OK = true ;
441
+ bool OK ;
442
442
443
443
if (!result )
444
444
OK = false;
@@ -450,11 +450,21 @@ AcceptResult(const PGresult *result)
450
450
case PGRES_EMPTY_QUERY :
451
451
case PGRES_COPY_IN :
452
452
case PGRES_COPY_OUT :
453
+ case PGRES_COPY_BOTH :
453
454
/* Fine, do nothing */
455
+ OK = true;
456
+ break ;
457
+
458
+ case PGRES_BAD_RESPONSE :
459
+ case PGRES_NONFATAL_ERROR :
460
+ case PGRES_FATAL_ERROR :
461
+ OK = false;
454
462
break ;
455
463
456
464
default :
457
465
OK = false;
466
+ psql_error ("unexpected PQresultStatus (%d)" ,
467
+ PQresultStatus (result ));
458
468
break ;
459
469
}
460
470
@@ -620,45 +630,114 @@ PrintQueryTuples(const PGresult *results)
620
630
621
631
622
632
/*
623
- * ProcessCopyResult: if command was a COPY FROM STDIN/TO STDOUT, handle it
633
+ * ProcessResult: utility function for use by SendQuery() only
624
634
*
625
- * Note: Utility function for use by SendQuery() only.
635
+ * When our command string contained a COPY FROM STDIN or COPY TO STDOUT,
636
+ * PQexec() has stopped at the PGresult associated with the first such
637
+ * command. In that event, we'll marshal data for the COPY and then cycle
638
+ * through any subsequent PGresult objects.
626
639
*
627
- * Returns true if the query executed successfully, false otherwise.
640
+ * When the command string contained no affected COPY command, this function
641
+ * degenerates to an AcceptResult() call.
642
+ *
643
+ * Changes its argument to point to the last PGresult of the command string,
644
+ * or NULL if that result was for a COPY FROM STDIN or COPY TO STDOUT.
645
+ *
646
+ * Returns true on complete success, false otherwise. Possible failure modes
647
+ * include purely client-side problems; check the transaction status for the
648
+ * server-side opinion.
628
649
*/
629
650
static bool
630
- ProcessCopyResult (PGresult * results )
651
+ ProcessResult (PGresult * * results )
631
652
{
632
- bool success = false;
653
+ PGresult * next_result ;
654
+ bool success = true;
655
+ bool first_cycle = true;
633
656
634
- if (!results )
635
- return false;
636
-
637
- switch (PQresultStatus (results ))
657
+ do
638
658
{
639
- case PGRES_TUPLES_OK :
640
- case PGRES_COMMAND_OK :
641
- case PGRES_EMPTY_QUERY :
642
- /* nothing to do here */
643
- success = true;
644
- break ;
659
+ ExecStatusType result_status ;
660
+ bool is_copy ;
645
661
646
- case PGRES_COPY_OUT :
647
- SetCancelConn ();
648
- success = handleCopyOut (pset .db , pset .queryFout );
649
- ResetCancelConn ();
662
+ if (!AcceptResult (* results ))
663
+ {
664
+ /*
665
+ * Failure at this point is always a server-side failure or a
666
+ * failure to submit the command string. Either way, we're
667
+ * finished with this command string.
668
+ */
669
+ success = false;
650
670
break ;
671
+ }
651
672
652
- case PGRES_COPY_IN :
673
+ result_status = PQresultStatus (* results );
674
+ switch (result_status )
675
+ {
676
+ case PGRES_COPY_BOTH :
677
+ /*
678
+ * No now-existing SQL command can yield PGRES_COPY_BOTH, but
679
+ * defend against the future. PQexec() can't short-circuit
680
+ * it's way out of a PGRES_COPY_BOTH, so the connection will
681
+ * be useless at this point. XXX is there a method for
682
+ * clearing this status that's likely to work with every
683
+ * future command that can initiate it?
684
+ */
685
+ psql_error ("unexpected PQresultStatus (%d)" , result_status );
686
+ return false;
687
+
688
+ case PGRES_COPY_OUT :
689
+ case PGRES_COPY_IN :
690
+ is_copy = true;
691
+ break ;
692
+
693
+ case PGRES_EMPTY_QUERY :
694
+ case PGRES_COMMAND_OK :
695
+ case PGRES_TUPLES_OK :
696
+ is_copy = false;
697
+ break ;
698
+
699
+ default :
700
+ /* AcceptResult() should have caught anything else. */
701
+ is_copy = false;
702
+ psql_error ("unexpected PQresultStatus (%d)" , result_status );
703
+ break ;
704
+ }
705
+
706
+ if (is_copy )
707
+ {
708
+ /*
709
+ * Marshal the COPY data. Either subroutine will get the
710
+ * connection out of its COPY state, then call PQresultStatus()
711
+ * once and report any error.
712
+ */
653
713
SetCancelConn ();
654
- success = handleCopyIn (pset .db , pset .cur_cmd_source ,
655
- PQbinaryTuples (results ));
714
+ if (result_status == PGRES_COPY_OUT )
715
+ success = handleCopyOut (pset .db , pset .queryFout ) && success ;
716
+ else
717
+ success = handleCopyIn (pset .db , pset .cur_cmd_source ,
718
+ PQbinaryTuples (* results )) && success ;
656
719
ResetCancelConn ();
657
- break ;
658
720
659
- default :
721
+ /*
722
+ * Call PQgetResult() once more. In the typical case of a
723
+ * single-command string, it will return NULL. Otherwise, we'll
724
+ * have other results to process that may include other COPYs.
725
+ */
726
+ PQclear (* results );
727
+ * results = next_result = PQgetResult (pset .db );
728
+ }
729
+ else if (first_cycle )
730
+ /* fast path: no COPY commands; PQexec visited all results */
660
731
break ;
661
- }
732
+ else if ((next_result = PQgetResult (pset .db )))
733
+ {
734
+ /* non-COPY command(s) after a COPY: keep the last one */
735
+ PQclear (* results );
736
+ * results = next_result ;
737
+ }
738
+
739
+ first_cycle = false;
740
+ } while (next_result );
662
741
663
742
/* may need this to recover from conn loss during COPY */
664
743
if (!CheckConnection ())
@@ -708,7 +787,7 @@ PrintQueryStatus(PGresult *results)
708
787
static bool
709
788
PrintQueryResults (PGresult * results )
710
789
{
711
- bool success = false ;
790
+ bool success ;
712
791
const char * cmdstatus ;
713
792
714
793
if (!results )
@@ -738,11 +817,21 @@ PrintQueryResults(PGresult *results)
738
817
739
818
case PGRES_COPY_OUT :
740
819
case PGRES_COPY_IN :
820
+ case PGRES_COPY_BOTH :
741
821
/* nothing to do here */
742
822
success = true;
743
823
break ;
744
824
825
+ case PGRES_BAD_RESPONSE :
826
+ case PGRES_NONFATAL_ERROR :
827
+ case PGRES_FATAL_ERROR :
828
+ success = false;
829
+ break ;
830
+
745
831
default :
832
+ success = false;
833
+ psql_error ("unexpected PQresultStatus (%d)" ,
834
+ PQresultStatus (results ));
746
835
break ;
747
836
}
748
837
@@ -867,7 +956,7 @@ SendQuery(const char *query)
867
956
868
957
/* these operations are included in the timing result: */
869
958
ResetCancelConn ();
870
- OK = ( AcceptResult ( results ) && ProcessCopyResult ( results ) );
959
+ OK = ProcessResult ( & results );
871
960
872
961
if (pset .timing )
873
962
{
@@ -877,7 +966,7 @@ SendQuery(const char *query)
877
966
}
878
967
879
968
/* but printing results isn't: */
880
- if (OK )
969
+ if (OK && results )
881
970
OK = PrintQueryResults (results );
882
971
}
883
972
else
@@ -891,34 +980,44 @@ SendQuery(const char *query)
891
980
/* If we made a temporary savepoint, possibly release/rollback */
892
981
if (on_error_rollback_savepoint )
893
982
{
894
- const char * svptcmd ;
983
+ const char * svptcmd = NULL ;
895
984
896
985
transaction_status = PQtransactionStatus (pset .db );
897
986
898
- if (transaction_status == PQTRANS_INERROR )
899
- {
900
- /* We always rollback on an error */
901
- svptcmd = "ROLLBACK TO pg_psql_temporary_savepoint" ;
902
- }
903
- else if (transaction_status != PQTRANS_INTRANS )
987
+ switch (transaction_status )
904
988
{
905
- /* If they are no longer in a transaction, then do nothing */
906
- svptcmd = NULL ;
907
- }
908
- else
909
- {
910
- /*
911
- * Do nothing if they are messing with savepoints themselves: If
912
- * the user did RELEASE or ROLLBACK, our savepoint is gone. If
913
- * they issued a SAVEPOINT, releasing ours would remove theirs.
914
- */
915
- if (results &&
916
- (strcmp (PQcmdStatus (results ), "SAVEPOINT" ) == 0 ||
917
- strcmp (PQcmdStatus (results ), "RELEASE" ) == 0 ||
918
- strcmp (PQcmdStatus (results ), "ROLLBACK" ) == 0 ))
919
- svptcmd = NULL ;
920
- else
921
- svptcmd = "RELEASE pg_psql_temporary_savepoint" ;
989
+ case PQTRANS_INERROR :
990
+ /* We always rollback on an error */
991
+ svptcmd = "ROLLBACK TO pg_psql_temporary_savepoint" ;
992
+ break ;
993
+
994
+ case PQTRANS_IDLE :
995
+ /* If they are no longer in a transaction, then do nothing */
996
+ break ;
997
+
998
+ case PQTRANS_INTRANS :
999
+ /*
1000
+ * Do nothing if they are messing with savepoints themselves:
1001
+ * If the user did RELEASE or ROLLBACK, our savepoint is
1002
+ * gone. If they issued a SAVEPOINT, releasing ours would
1003
+ * remove theirs.
1004
+ */
1005
+ if (results &&
1006
+ (strcmp (PQcmdStatus (results ), "SAVEPOINT" ) == 0 ||
1007
+ strcmp (PQcmdStatus (results ), "RELEASE" ) == 0 ||
1008
+ strcmp (PQcmdStatus (results ), "ROLLBACK" ) == 0 ))
1009
+ svptcmd = NULL ;
1010
+ else
1011
+ svptcmd = "RELEASE pg_psql_temporary_savepoint" ;
1012
+ break ;
1013
+
1014
+ case PQTRANS_ACTIVE :
1015
+ case PQTRANS_UNKNOWN :
1016
+ default :
1017
+ OK = false;
1018
+ psql_error ("unexpected transaction status (%d)\n" ,
1019
+ transaction_status );
1020
+ break ;
922
1021
}
923
1022
924
1023
if (svptcmd )
0 commit comments