33 *
44 * Copyright (c) 2000-2005, PostgreSQL Global Development Group
55 *
6- * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.96 2005/02/22 04:40:52 momjian Exp $
6+ * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.97 2005/04/28 13:09:59 momjian Exp $
77 */
88#include "postgres_fe.h"
99#include "common.h"
@@ -941,11 +941,13 @@ PrintQueryResults(PGresult *results)
941941bool
942942SendQuery (const char * query )
943943{
944- PGresult * results ;
945- TimevalStruct before ,
946- after ;
947- bool OK ;
948-
944+ PGresult * results ;
945+ TimevalStruct before ,after ;
946+ bool OK ,on_error_rollback_savepoint = false;
947+ PGTransactionStatusType transaction_status ;
948+ static bool on_error_rollback_warning = false;
949+ const char * rollback_str ;
950+
949951if (!pset .db )
950952{
951953psql_error ("You are currently not connected to a database.\n" );
@@ -973,7 +975,9 @@ SendQuery(const char *query)
973975
974976SetCancelConn ();
975977
976- if (PQtransactionStatus (pset .db )== PQTRANS_IDLE &&
978+ transaction_status = PQtransactionStatus (pset .db );
979+
980+ if (transaction_status == PQTRANS_IDLE &&
977981!GetVariableBool (pset .vars ,"AUTOCOMMIT" )&&
978982!command_no_begin (query ))
979983{
@@ -987,6 +991,33 @@ SendQuery(const char *query)
987991}
988992PQclear (results );
989993}
994+ else if (transaction_status == PQTRANS_INTRANS &&
995+ (rollback_str = GetVariable (pset .vars ,"ON_ERROR_ROLLBACK" ))!= NULL &&
996+ /* !off and !interactive is 'on' */
997+ pg_strcasecmp (rollback_str ,"off" )!= 0 &&
998+ (pset .cur_cmd_interactive ||
999+ pg_strcasecmp (rollback_str ,"interactive" )!= 0 ))
1000+ {
1001+ if (on_error_rollback_warning == false&& pset .sversion < 80000 )
1002+ {
1003+ fprintf (stderr ,_ ("The server version (%d) does not support savepoints for ON_ERROR_ROLLBACK.\n" ),
1004+ pset .sversion );
1005+ on_error_rollback_warning = true;
1006+ }
1007+ else
1008+ {
1009+ results = PQexec (pset .db ,"SAVEPOINT pg_psql_temporary_savepoint" );
1010+ if (PQresultStatus (results )!= PGRES_COMMAND_OK )
1011+ {
1012+ psql_error ("%s" ,PQerrorMessage (pset .db ));
1013+ PQclear (results );
1014+ ResetCancelConn ();
1015+ return false;
1016+ }
1017+ PQclear (results );
1018+ on_error_rollback_savepoint = true;
1019+ }
1020+ }
9901021
9911022if (pset .timing )
9921023GETTIMEOFDAY (& before );
@@ -1005,6 +1036,41 @@ SendQuery(const char *query)
10051036
10061037PQclear (results );
10071038
1039+ /* If we made a temporary savepoint, possibly release/rollback */
1040+ if (on_error_rollback_savepoint )
1041+ {
1042+ transaction_status = PQtransactionStatus (pset .db );
1043+
1044+ /* We always rollback on an error */
1045+ if (transaction_status == PQTRANS_INERROR )
1046+ results = PQexec (pset .db ,"ROLLBACK TO pg_psql_temporary_savepoint" );
1047+ /* If they are no longer in a transaction, then do nothing */
1048+ else if (transaction_status != PQTRANS_INTRANS )
1049+ results = NULL ;
1050+ else
1051+ {
1052+ /*
1053+ *Do nothing if they are messing with savepoints themselves:
1054+ *If the user did RELEASE or ROLLBACK, our savepoint is gone.
1055+ *If they issued a SAVEPOINT, releasing ours would remove theirs.
1056+ */
1057+ if (strcmp (PQcmdStatus (results ),"SAVEPOINT" )== 0 ||
1058+ strcmp (PQcmdStatus (results ),"RELEASE" )== 0 ||
1059+ strcmp (PQcmdStatus (results ),"ROLLBACK" )== 0 )
1060+ results = NULL ;
1061+ else
1062+ results = PQexec (pset .db ,"RELEASE pg_psql_temporary_savepoint" );
1063+ }
1064+ if (PQresultStatus (results )!= PGRES_COMMAND_OK )
1065+ {
1066+ psql_error ("%s" ,PQerrorMessage (pset .db ));
1067+ PQclear (results );
1068+ ResetCancelConn ();
1069+ return false;
1070+ }
1071+ PQclear (results );
1072+ }
1073+
10081074/* Possible microtiming output */
10091075if (OK && pset .timing )
10101076printf (_ ("Time: %.3f ms\n" ),DIFF_MSEC (& after ,& before ));