1 #include "common.h"
2
3 #include <common/test_assert.h>
4
5 /* Test transaction types */
6
7 static void
ReadErrorConn(void)8 ReadErrorConn(void)
9 {
10 ODBC_BUF *odbc_buf = NULL;
11 SQLTCHAR *err = (SQLTCHAR *) ODBC_GET(sizeof(odbc_err)*sizeof(SQLTCHAR));
12 SQLTCHAR *state = (SQLTCHAR *) ODBC_GET(sizeof(odbc_sqlstate)*sizeof(SQLTCHAR));
13
14 memset(odbc_err, 0, sizeof(odbc_err));
15 memset(odbc_sqlstate, 0, sizeof(odbc_sqlstate));
16 CHKGetDiagRec(SQL_HANDLE_DBC, odbc_conn, 1, state, NULL, err, sizeof(odbc_err), NULL, "SI");
17 strcpy(odbc_err, C(err));
18 strcpy(odbc_sqlstate, C(state));
19 ODBC_FREE();
20 printf("Message: '%s' %s\n", odbc_sqlstate, odbc_err);
21 }
22
23 static void
AutoCommit(int onoff)24 AutoCommit(int onoff)
25 {
26 CHKSetConnectAttr(SQL_ATTR_AUTOCOMMIT, int2ptr(onoff), 0, "S");
27 }
28
29 static void
EndTransaction(SQLSMALLINT type)30 EndTransaction(SQLSMALLINT type)
31 {
32 CHKEndTran(SQL_HANDLE_DBC, odbc_conn, type, "S");
33 }
34
35 #define SWAP(t,a,b) do { t xyz = a; a = b; b = xyz; } while(0)
36 #define SWAP_CONN() do { SWAP(HENV,env,odbc_env); SWAP(HDBC,dbc,odbc_conn); SWAP(HSTMT,stmt,odbc_stmt);} while(0)
37
38 static HENV env = SQL_NULL_HENV;
39 static HDBC dbc = SQL_NULL_HDBC;
40 static HSTMT stmt = SQL_NULL_HSTMT;
41
42 static int
CheckDirtyRead(void)43 CheckDirtyRead(void)
44 {
45 SQLRETURN RetCode;
46
47 /* transaction 1 try to change a row but not commit */
48 odbc_command("UPDATE test_transaction SET t = 'second' WHERE n = 1");
49
50 SWAP_CONN();
51
52 /* second transaction try to fetch uncommited row */
53 RetCode = odbc_command2("SELECT * FROM test_transaction WHERE t = 'second' AND n = 1", "SE");
54 if (RetCode == SQL_ERROR) {
55 EndTransaction(SQL_ROLLBACK);
56 SWAP_CONN();
57 EndTransaction(SQL_ROLLBACK);
58 return 0; /* no dirty read */
59 }
60
61 CHKFetch("S");
62 CHKFetch("No");
63 SQLMoreResults(odbc_stmt);
64 EndTransaction(SQL_ROLLBACK);
65 SWAP_CONN();
66 EndTransaction(SQL_ROLLBACK);
67 return 1;
68 }
69
70 static int
CheckNonrepeatableRead(void)71 CheckNonrepeatableRead(void)
72 {
73 SQLRETURN RetCode;
74
75 /* transaction 2 read a row */
76 SWAP_CONN();
77 odbc_command("SELECT * FROM test_transaction WHERE t = 'initial' AND n = 1");
78 SQLMoreResults(odbc_stmt);
79
80 /* transaction 1 change a row and commit */
81 SWAP_CONN();
82 RetCode = odbc_command2("UPDATE test_transaction SET t = 'second' WHERE n = 1", "SE");
83 if (RetCode == SQL_ERROR) {
84 EndTransaction(SQL_ROLLBACK);
85 SWAP_CONN();
86 EndTransaction(SQL_ROLLBACK);
87 SWAP_CONN();
88 return 0; /* no dirty read */
89 }
90 EndTransaction(SQL_COMMIT);
91
92 SWAP_CONN();
93
94 /* second transaction try to fetch commited row */
95 odbc_command("SELECT * FROM test_transaction WHERE t = 'second' AND n = 1");
96
97 CHKFetch("S");
98 CHKFetch("No");
99 SQLMoreResults(odbc_stmt);
100 EndTransaction(SQL_ROLLBACK);
101 SWAP_CONN();
102 odbc_command("UPDATE test_transaction SET t = 'initial' WHERE n = 1");
103 EndTransaction(SQL_COMMIT);
104 return 1;
105 }
106
107 static int
CheckPhantom(void)108 CheckPhantom(void)
109 {
110 SQLRETURN RetCode;
111
112 /* transaction 2 read a row */
113 SWAP_CONN();
114 odbc_command("SELECT * FROM test_transaction WHERE t = 'initial'");
115 SQLMoreResults(odbc_stmt);
116
117 /* transaction 1 insert a row that match critera */
118 SWAP_CONN();
119 RetCode = odbc_command2("INSERT INTO test_transaction(n, t) VALUES(2, 'initial')", "SE");
120 if (RetCode == SQL_ERROR) {
121 EndTransaction(SQL_ROLLBACK);
122 SWAP_CONN();
123 EndTransaction(SQL_ROLLBACK);
124 SWAP_CONN();
125 return 0; /* no dirty read */
126 }
127 EndTransaction(SQL_COMMIT);
128
129 SWAP_CONN();
130
131 /* second transaction try to fetch commited row */
132 odbc_command("SELECT * FROM test_transaction WHERE t = 'initial'");
133
134 CHKFetch("S");
135 CHKFetch("S");
136 CHKFetch("No");
137 SQLMoreResults(odbc_stmt);
138 EndTransaction(SQL_ROLLBACK);
139 SWAP_CONN();
140 odbc_command("DELETE test_transaction WHERE n = 2");
141 EndTransaction(SQL_COMMIT);
142 return 1;
143 }
144
145 static int test_with_connect = 0;
146
147 static int global_txn;
148
149 static int hide_error;
150
151 static void
my_attrs(void)152 my_attrs(void)
153 {
154 CHKSetConnectAttr(SQL_ATTR_TXN_ISOLATION, int2ptr(global_txn), 0, "S");
155 AutoCommit(SQL_AUTOCOMMIT_OFF);
156 }
157
158 static void
ConnectWithTxn(int txn)159 ConnectWithTxn(int txn)
160 {
161 global_txn = txn;
162 odbc_set_conn_attr = my_attrs;
163 odbc_connect();
164 odbc_set_conn_attr = NULL;
165 }
166
167 static int
Test(int txn,const char * expected)168 Test(int txn, const char *expected)
169 {
170 int dirty, repeatable, phantom;
171 char buf[128];
172
173 SWAP_CONN();
174 if (test_with_connect) {
175 odbc_disconnect();
176 ConnectWithTxn(txn);
177 CHKSetStmtAttr(SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER) 2, 0, "S");
178 } else {
179 CHKSetConnectAttr(SQL_ATTR_TXN_ISOLATION, int2ptr(txn), 0, "S");
180 }
181 SWAP_CONN();
182
183 dirty = CheckDirtyRead();
184 repeatable = CheckNonrepeatableRead();
185 phantom = CheckPhantom();
186
187 sprintf(buf, "dirty %d non repeatable %d phantom %d", dirty, repeatable, phantom);
188 if (strcmp(buf, expected) != 0) {
189 if (hide_error) {
190 hide_error = 0;
191 return 0;
192 }
193 fprintf(stderr, "detected wrong TXN\nexpected '%s' got '%s'\n", expected, buf);
194 exit(1);
195 }
196 hide_error = 0;
197 return 1;
198 }
199
200 int
main(int argc,char * argv[])201 main(int argc, char *argv[])
202 {
203 odbc_use_version3 = 1;
204 odbc_connect();
205
206 /* Invalid argument value */
207 CHKSetConnectAttr(SQL_ATTR_TXN_ISOLATION, int2ptr(SQL_TXN_REPEATABLE_READ | SQL_TXN_READ_COMMITTED), 0, "E");
208 ReadErrorConn();
209 if (strcmp(odbc_sqlstate, "HY024") != 0) {
210 odbc_disconnect();
211 fprintf(stderr, "Unexpected success\n");
212 return 1;
213 }
214
215 /* here we can't use temporary table cause we use two connection */
216 odbc_command("IF OBJECT_ID('test_transaction') IS NOT NULL DROP TABLE test_transaction");
217 odbc_command("CREATE TABLE test_transaction(n NUMERIC(18,0) PRIMARY KEY, t VARCHAR(30))");
218
219 CHKSetStmtAttr(SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER) 2, 0, "S");
220
221 AutoCommit(SQL_AUTOCOMMIT_OFF);
222 odbc_command("INSERT INTO test_transaction(n, t) VALUES(1, 'initial')");
223
224 #ifdef ENABLE_DEVELOPING
225 /* test setting with active transaction "Operation invalid at this time" */
226 CHKSetConnectAttr(SQL_ATTR_TXN_ISOLATION, int2ptr(SQL_TXN_REPEATABLE_READ), 0, "E");
227 ReadErrorConn();
228 if (strcmp(odbc_sqlstate, "HY011") != 0) {
229 odbc_disconnect();
230 fprintf(stderr, "Unexpected success\n");
231 return 1;
232 }
233 #endif
234
235 EndTransaction(SQL_COMMIT);
236
237 odbc_command("SELECT * FROM test_transaction");
238
239 /* test setting with pending data */
240 CHKSetConnectAttr(SQL_ATTR_TXN_ISOLATION, int2ptr(SQL_TXN_REPEATABLE_READ), 0, "E");
241 ReadErrorConn();
242 if (strcmp(odbc_sqlstate, "HY011") != 0) {
243 odbc_disconnect();
244 fprintf(stderr, "Unexpected success\n");
245 return 1;
246 }
247
248 SQLMoreResults(odbc_stmt);
249
250 EndTransaction(SQL_COMMIT);
251
252
253 /* save this connection and do another */
254 SWAP_CONN();
255
256 odbc_connect();
257
258 CHKSetStmtAttr(SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER) 2, 0, "S");
259 AutoCommit(SQL_AUTOCOMMIT_OFF);
260
261 SWAP_CONN();
262
263 for (test_with_connect = 0; test_with_connect <= 1; ++test_with_connect) {
264 Test(SQL_TXN_READ_UNCOMMITTED, "dirty 1 non repeatable 1 phantom 1");
265 Test(SQL_TXN_READ_COMMITTED, "dirty 0 non repeatable 1 phantom 1");
266 if (odbc_db_is_microsoft()) {
267 Test(SQL_TXN_REPEATABLE_READ, "dirty 0 non repeatable 0 phantom 1");
268 } else {
269 hide_error = 1;
270 if (!Test(SQL_TXN_REPEATABLE_READ, "dirty 0 non repeatable 0 phantom 1"))
271 Test(SQL_TXN_REPEATABLE_READ, "dirty 0 non repeatable 0 phantom 0");
272 }
273 Test(SQL_TXN_SERIALIZABLE, "dirty 0 non repeatable 0 phantom 0");
274 }
275
276 odbc_disconnect();
277
278 SWAP_CONN();
279
280 EndTransaction(SQL_COMMIT);
281
282 /* Sybase do not accept DROP TABLE during a transaction */
283 AutoCommit(SQL_AUTOCOMMIT_ON);
284 odbc_command("DROP TABLE test_transaction");
285
286 odbc_disconnect();
287 return 0;
288 }
289