|
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 |
|
|