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