Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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)