|
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 | externboolprompt_state; |
63 | 63 |
|
64 | 64 |
|
65 | | -staticboolis_transact_command(constchar*query); |
| 65 | +staticboolcommand_no_begin(constchar*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 | returnOK; |
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 | | -staticbool |
953 | | -is_transact_command(constchar*query) |
| 953 | +staticconstchar* |
| 954 | +skip_white_space(constchar*query) |
954 | 955 | { |
955 | | -intwordlen; |
| 956 | +intcnestlevel=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 | +intmblen=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((unsignedchar)*query)) |
963 | | -query++; |
964 | | -elseif (query[0]=='-'&&query[1]=='-') |
| 971 | +query+=mblen; |
| 972 | +elseif (query[0]=='/'&&query[1]=='*') |
965 | 973 | { |
| 974 | +cnestlevel++; |
966 | 975 | query+=2; |
967 | | -while (*query&&*query!='\n') |
968 | | -query++; |
969 | 976 | } |
970 | | -elseif (query[0]=='/'&&query[1]=='*') |
| 977 | +elseif (cnestlevel>0&&query[0]=='*'&&query[1]=='/') |
| 978 | +{ |
| 979 | +cnestlevel--; |
| 980 | +query+=2; |
| 981 | +} |
| 982 | +elseif (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 | +elseif (cnestlevel>0) |
| 1000 | +query+=mblen; |
984 | 1001 | else |
985 | 1002 | break;/* found first token */ |
986 | 1003 | } |
987 | 1004 |
|
| 1005 | +returnquery; |
| 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 | +staticbool |
| 1017 | +command_no_begin(constchar*query) |
| 1018 | +{ |
| 1019 | +intwordlen; |
| 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((unsignedchar)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((unsignedchar)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 |
|
|