|
3 | 3 | *
|
4 | 4 | * Copyright (c) 2000-2004, PostgreSQL Global Development Group
|
5 | 5 | *
|
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 $ |
7 | 7 | */
|
8 | 8 | #include "postgres_fe.h"
|
9 | 9 | #include "common.h"
|
@@ -62,7 +62,7 @@ typedef struct _timeb TimevalStruct;
|
62 | 62 | extern bool prompt_state;
|
63 | 63 |
|
64 | 64 |
|
65 |
| -static bool is_transact_command(const char *query); |
| 65 | +static bool command_no_begin(const char *query); |
66 | 66 |
|
67 | 67 |
|
68 | 68 | /*
|
@@ -895,7 +895,7 @@ SendQuery(const char *query)
|
895 | 895 |
|
896 | 896 | if (PQtransactionStatus(pset.db) == PQTRANS_IDLE &&
|
897 | 897 | !GetVariableBool(pset.vars, "AUTOCOMMIT") &&
|
898 |
| - !is_transact_command(query)) |
| 898 | + !command_no_begin(query)) |
899 | 899 | {
|
900 | 900 | results = PQexec(pset.db, "BEGIN");
|
901 | 901 | if (PQresultStatus(results) != PGRES_COMMAND_OK)
|
@@ -946,65 +946,147 @@ SendQuery(const char *query)
|
946 | 946 | return OK;
|
947 | 947 | }
|
948 | 948 |
|
| 949 | + |
949 | 950 | /*
|
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. |
951 | 952 | */
|
952 |
| -static bool |
953 |
| -is_transact_command(const char *query) |
| 953 | +static const char * |
| 954 | +skip_white_space(const char *query) |
954 | 955 | {
|
955 |
| - int wordlen; |
| 956 | + int cnestlevel = 0; /* slash-star comment nest level */ |
956 | 957 |
|
957 |
| - /* |
958 |
| - * First we must advance over any whitespace and comments. |
959 |
| - */ |
960 | 958 | while (*query)
|
961 | 959 | {
|
| 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 | + */ |
962 | 970 | if (isspace((unsigned char) *query))
|
963 |
| - query++; |
964 |
| - else if (query[0] == '-' && query[1] == '-') |
| 971 | + query += mblen; |
| 972 | + else if (query[0] == '/' && query[1] == '*') |
965 | 973 | {
|
| 974 | + cnestlevel++; |
966 | 975 | query += 2;
|
967 |
| - while (*query && *query != '\n') |
968 |
| - query++; |
969 | 976 | }
|
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] == '-') |
971 | 983 | {
|
972 | 984 | 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 | + */ |
973 | 989 | while (*query)
|
974 | 990 | {
|
975 |
| - if (query[0] == '*' && query[1] == '/') |
| 991 | + if (*query == '\n') |
976 | 992 | {
|
977 |
| - query += 2; |
| 993 | + query++; |
978 | 994 | break;
|
979 | 995 | }
|
980 |
| - else |
981 |
| - query++; |
| 996 | + query += PQmblen(query, pset.encoding); |
982 | 997 | }
|
983 | 998 | }
|
| 999 | + else if (cnestlevel > 0) |
| 1000 | + query += mblen; |
984 | 1001 | else
|
985 | 1002 | break; /* found first token */
|
986 | 1003 | }
|
987 | 1004 |
|
| 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 | + |
988 | 1021 | /*
|
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"). |
990 | 1028 | */
|
991 | 1029 | wordlen = 0;
|
992 | 1030 | while (isalpha((unsigned char) query[wordlen]))
|
993 |
| - wordlen++; |
| 1031 | + wordlen += PQmblen(&query[wordlen], pset.encoding); |
994 | 1032 |
|
| 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; |
995 | 1043 | if (wordlen == 5 && pg_strncasecmp(query, "begin", 5) == 0)
|
996 | 1044 | return true;
|
| 1045 | + if (wordlen == 5 && pg_strncasecmp(query, "start", 5) == 0) |
| 1046 | + return true; |
997 | 1047 | if (wordlen == 6 && pg_strncasecmp(query, "commit", 6) == 0)
|
998 | 1048 | return true;
|
999 |
| - if (wordlen == 8 && pg_strncasecmp(query, "rollback", 8) == 0) |
| 1049 | + if (wordlen == 3 && pg_strncasecmp(query, "end", 3) == 0) |
1000 | 1050 | return true;
|
1001 |
| - if (wordlen == 5 && pg_strncasecmp(query, "abort", 5) == 0) |
| 1051 | + if (wordlen == 8 && pg_strncasecmp(query, "rollback", 8) == 0) |
1002 | 1052 | 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) |
1004 | 1063 | return true;
|
1005 |
| - if (wordlen == 5 && pg_strncasecmp(query, "start", 5) == 0) |
| 1064 | + if (wordlen == 7 && pg_strncasecmp(query, "cluster", 7) == 0) |
1006 | 1065 | return true;
|
1007 | 1066 |
|
| 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 | + |
1008 | 1090 | return false;
|
1009 | 1091 | }
|
1010 | 1092 |
|
|
0 commit comments