Skip to content

Commit 5b564e5

Browse files
committed
Fix some shortcomings in psql's autocommit-off mode concerning detection
of commands for which a transaction block should not be forced. Recognize VACUUM and other PreventTransactionChain commands; handle nested /* .. */ comments correctly; handle multibyte encodings correctly. Michael Paesold with some kibitzing from Tom Lane.
1 parent 9332d0b commit 5b564e5

File tree

2 files changed

+110
-27
lines changed

2 files changed

+110
-27
lines changed

doc/src/sgml/ref/psql-ref.sgml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.121 2004/08/24 00:06:51 neilc Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.122 2004/09/20 18:51:17 tgl Exp $
33
PostgreSQL documentation
44
-->
55

@@ -1875,7 +1875,8 @@ bar
18751875
mode works by issuing an implicit <command>BEGIN</> for you, just
18761876
before any command that is not already in a transaction block and
18771877
is not itself a <command>BEGIN</> or other transaction-control
1878-
command.
1878+
command, nor a command that cannot be executed inside a transaction
1879+
block (such as <command>VACUUM</>).
18791880
</para>
18801881

18811882
<note>

src/bin/psql/common.c

Lines changed: 107 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Copyright (c) 2000-2004, PostgreSQL Global Development Group
55
*
6-
* $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.90 2004/08/29 05:06:54 momjian Exp $
6+
* $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.91 2004/09/20 18:51:19 tgl Exp $
77
*/
88
#include "postgres_fe.h"
99
#include "common.h"
@@ -62,7 +62,7 @@ typedef struct _timeb TimevalStruct;
6262
extern bool prompt_state;
6363

6464

65-
static bool is_transact_command(const char *query);
65+
static bool command_no_begin(const char *query);
6666

6767

6868
/*
@@ -895,7 +895,7 @@ SendQuery(const char *query)
895895

896896
if (PQtransactionStatus(pset.db) == PQTRANS_IDLE &&
897897
!GetVariableBool(pset.vars, "AUTOCOMMIT") &&
898-
!is_transact_command(query))
898+
!command_no_begin(query))
899899
{
900900
results = PQexec(pset.db, "BEGIN");
901901
if (PQresultStatus(results) != PGRES_COMMAND_OK)
@@ -946,65 +946,147 @@ SendQuery(const char *query)
946946
return OK;
947947
}
948948

949+
949950
/*
950-
* check whether a query string begins with BEGIN/COMMIT/ROLLBACK/START XACT
951+
* Advance the given char pointer over white space and SQL comments.
951952
*/
952-
static bool
953-
is_transact_command(const char *query)
953+
static const char *
954+
skip_white_space(const char *query)
954955
{
955-
int wordlen;
956+
int cnestlevel = 0; /* slash-star comment nest level */
956957

957-
/*
958-
* First we must advance over any whitespace and comments.
959-
*/
960958
while (*query)
961959
{
960+
int mblen = PQmblen(query, pset.encoding);
961+
962+
/*
963+
* Note: we assume the encoding is a superset of ASCII, so that
964+
* for example "query[0] == '/'" is meaningful. However, we do NOT
965+
* assume that the second and subsequent bytes of a multibyte
966+
* character couldn't look like ASCII characters; so it is critical
967+
* to advance by mblen, not 1, whenever we haven't exactly identified
968+
* the character we are skipping over.
969+
*/
962970
if (isspace((unsigned char) *query))
963-
query++;
964-
else if (query[0] == '-' && query[1] == '-')
971+
query += mblen;
972+
else if (query[0] == '/' && query[1] == '*')
965973
{
974+
cnestlevel++;
966975
query += 2;
967-
while (*query && *query != '\n')
968-
query++;
969976
}
970-
else if (query[0] == '/' && query[1] == '*')
977+
else if (cnestlevel > 0 && query[0] == '*' && query[1] == '/')
978+
{
979+
cnestlevel--;
980+
query += 2;
981+
}
982+
else if (cnestlevel == 0 && query[0] == '-' && query[1] == '-')
971983
{
972984
query += 2;
985+
/*
986+
* We have to skip to end of line since any slash-star inside
987+
* the -- comment does NOT start a slash-star comment.
988+
*/
973989
while (*query)
974990
{
975-
if (query[0] == '*' && query[1] == '/')
991+
if (*query == '\n')
976992
{
977-
query += 2;
993+
query++;
978994
break;
979995
}
980-
else
981-
query++;
996+
query += PQmblen(query, pset.encoding);
982997
}
983998
}
999+
else if (cnestlevel > 0)
1000+
query += mblen;
9841001
else
9851002
break; /* found first token */
9861003
}
9871004

1005+
return query;
1006+
}
1007+
1008+
1009+
/*
1010+
* Check whether a command is one of those for which we should NOT start
1011+
* a new transaction block (ie, send a preceding BEGIN).
1012+
*
1013+
* These include the transaction control statements themselves, plus
1014+
* certain statements that the backend disallows inside transaction blocks.
1015+
*/
1016+
static bool
1017+
command_no_begin(const char *query)
1018+
{
1019+
int wordlen;
1020+
9881021
/*
989-
* Check word length ("beginx" is not "begin").
1022+
* First we must advance over any whitespace and comments.
1023+
*/
1024+
query = skip_white_space(query);
1025+
1026+
/*
1027+
* Check word length (since "beginx" is not "begin").
9901028
*/
9911029
wordlen = 0;
9921030
while (isalpha((unsigned char) query[wordlen]))
993-
wordlen++;
1031+
wordlen += PQmblen(&query[wordlen], pset.encoding);
9941032

1033+
/*
1034+
* Transaction control commands. These should include every keyword
1035+
* that gives rise to a TransactionStmt in the backend grammar, except
1036+
* for the savepoint-related commands.
1037+
*
1038+
* (We assume that START must be START TRANSACTION, since there is
1039+
* presently no other "START foo" command.)
1040+
*/
1041+
if (wordlen == 5 && pg_strncasecmp(query, "abort", 5) == 0)
1042+
return true;
9951043
if (wordlen == 5 && pg_strncasecmp(query, "begin", 5) == 0)
9961044
return true;
1045+
if (wordlen == 5 && pg_strncasecmp(query, "start", 5) == 0)
1046+
return true;
9971047
if (wordlen == 6 && pg_strncasecmp(query, "commit", 6) == 0)
9981048
return true;
999-
if (wordlen == 8 && pg_strncasecmp(query, "rollback", 8) == 0)
1049+
if (wordlen == 3 && pg_strncasecmp(query, "end", 3) == 0)
10001050
return true;
1001-
if (wordlen == 5 && pg_strncasecmp(query, "abort", 5) == 0)
1051+
if (wordlen == 8 && pg_strncasecmp(query, "rollback", 8) == 0)
10021052
return true;
1003-
if (wordlen == 3 && pg_strncasecmp(query, "end", 3) == 0)
1053+
1054+
/*
1055+
* Commands not allowed within transactions. The statements checked
1056+
* for here should be exactly those that call PreventTransactionChain()
1057+
* in the backend.
1058+
*
1059+
* Note: we are a bit sloppy about CLUSTER, which is transactional in
1060+
* some variants but not others.
1061+
*/
1062+
if (wordlen == 6 && pg_strncasecmp(query, "vacuum", 6) == 0)
10041063
return true;
1005-
if (wordlen == 5 && pg_strncasecmp(query, "start", 5) == 0)
1064+
if (wordlen == 7 && pg_strncasecmp(query, "cluster", 7) == 0)
10061065
return true;
10071066

1067+
/*
1068+
* Note: these tests will match REINDEX TABLESPACE, which isn't really
1069+
* a valid command so we don't care much. The other five possible
1070+
* matches are correct.
1071+
*/
1072+
if ((wordlen == 6 && pg_strncasecmp(query, "create", 6) == 0) ||
1073+
(wordlen == 4 && pg_strncasecmp(query, "drop", 4) == 0) ||
1074+
(wordlen == 7 && pg_strncasecmp(query, "reindex", 7) == 0))
1075+
{
1076+
query += wordlen;
1077+
1078+
query = skip_white_space(query);
1079+
1080+
wordlen = 0;
1081+
while (isalpha((unsigned char) query[wordlen]))
1082+
wordlen += PQmblen(&query[wordlen], pset.encoding);
1083+
1084+
if (wordlen == 8 && pg_strncasecmp(query, "database", 8) == 0)
1085+
return true;
1086+
if (wordlen == 10 && pg_strncasecmp(query, "tablespace", 10) == 0)
1087+
return true;
1088+
}
1089+
10081090
return false;
10091091
}
10101092

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy