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