Line data Source code
1 : /* Check some internal token behaviour */
2 :
3 : #include "common.h"
4 :
5 : #include <assert.h>
6 :
7 : #if HAVE_UNISTD_H
8 : #include <unistd.h>
9 : #endif /* HAVE_UNISTD_H */
10 :
11 : #include <freetds/time.h>
12 :
13 : #if HAVE_ERRNO_H
14 : #include <errno.h>
15 : #endif /* HAVE_ERRNO_H */
16 :
17 : #if HAVE_SYS_SOCKET_H
18 : #include <sys/socket.h>
19 : #endif /* HAVE_SYS_SOCKET_H */
20 :
21 : #if HAVE_NETINET_IN_H
22 : #include <netinet/in.h>
23 : #endif /* HAVE_NETINET_IN_H */
24 :
25 : #include <freetds/tds.h>
26 : #include <freetds/replacements.h>
27 : #include <freetds/server.h>
28 : #include <freetds/utils.h>
29 :
30 : #include "fake_thread.h"
31 : #include "parser.h"
32 :
33 : #if TDS_HAVE_MUTEX
34 :
35 : #ifdef _WIN32
36 : #define SHUT_RDWR SD_BOTH
37 : #endif
38 :
39 : static void parse_sql(TDSSOCKET * tds, const char *sql);
40 :
41 : static void
42 10 : setup_override(void)
43 : {
44 10 : assert(common_pwd.initialized);
45 10 : strcpy(common_pwd.user, "guest");
46 10 : strcpy(common_pwd.password, "sybase");
47 10 : strcpy(common_pwd.database, "tempdb");
48 :
49 10 : unsetenv("TDSINIOVERRIDE");
50 :
51 10 : unsetenv("TDSHOST");
52 10 : unsetenv("TDSPORT");
53 10 : unsetenv("TDSVER");
54 10 : }
55 :
56 : static TDSSOCKET *
57 20 : tds_from_sock(TDSCONTEXT *ctx, TDS_SYS_SOCKET fd)
58 : {
59 : TDSSOCKET *tds;
60 :
61 20 : tds = tds_alloc_socket(ctx, 4096);
62 20 : if (!tds) {
63 0 : CLOSESOCKET(fd);
64 0 : fprintf(stderr, "out of memory");
65 0 : return NULL;
66 : }
67 20 : tds_set_s(tds, fd);
68 20 : tds->out_flag = TDS_LOGIN;
69 : /* TODO proper charset */
70 20 : tds_iconv_open(tds->conn, "ISO8859-1", 0);
71 : /* get_incoming(tds->s); */
72 20 : tds->state = TDS_IDLE;
73 :
74 20 : tds->conn->client_spid = 0x33;
75 20 : tds->conn->product_version = TDS_MS_VER(10, 0, 6000);
76 :
77 20 : return tds;
78 : }
79 :
80 : static void handle_one(TDS_SYS_SOCKET sock);
81 : static TDS_SYS_SOCKET stop_socket = INVALID_SOCKET;
82 :
83 : /* accept a socket and emulate a server */
84 10 : TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg)
85 : {
86 10 : TDS_SYS_SOCKET s = TDS_PTR2INT(arg), sock;
87 : socklen_t len;
88 : struct sockaddr_in sin;
89 : struct pollfd fds[2];
90 : TDS_SYS_SOCKET sockets[2];
91 :
92 10 : assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) >= 0);
93 10 : stop_socket = sockets[1];
94 :
95 : for (;;) {
96 50 : fds[0].fd = s;
97 30 : fds[0].events = POLLIN;
98 30 : fds[0].revents = 0;
99 30 : fds[1].fd = sockets[0];
100 30 : fds[1].events = POLLIN;
101 30 : fds[1].revents = 0;
102 30 : if (poll(fds, 2, 30000) <= 0) {
103 0 : fprintf(stderr, "poll: %d\n", sock_errno);
104 0 : exit(1);
105 : }
106 30 : if (fds[1].revents)
107 : break;
108 :
109 20 : memset(&sin, 0, sizeof(sin));
110 20 : len = sizeof(sin);
111 20 : if (TDS_IS_SOCKET_INVALID(sock = tds_accept(s, (struct sockaddr *) &sin, &len))) {
112 0 : perror("accept");
113 0 : exit(1);
114 : }
115 20 : tds_socket_set_nodelay(sock);
116 20 : handle_one(sock);
117 : }
118 10 : CLOSESOCKET(s);
119 10 : CLOSESOCKET(sockets[0]);
120 10 : CLOSESOCKET(sockets[1]);
121 :
122 10 : return TDS_THREAD_RESULT(0);
123 : }
124 :
125 : static void
126 20 : handle_one(TDS_SYS_SOCKET sock)
127 : {
128 : TDSSOCKET *tds;
129 : TDSLOGIN *login;
130 : TDSCONTEXT *ctx;
131 :
132 20 : ctx = tds_alloc_context(NULL);
133 20 : if (!ctx)
134 0 : exit(1);
135 :
136 20 : tds = tds_from_sock(ctx, sock);
137 20 : if (!tds)
138 0 : exit(1);
139 :
140 20 : login = tds_alloc_read_login(tds);
141 20 : if (!login) {
142 0 : fprintf(stderr, "Error reading login\n");
143 0 : exit(1);
144 : }
145 :
146 60 : if (!strcmp(tds_dstr_cstr(&login->user_name), "guest") && !strcmp(tds_dstr_cstr(&login->password), "sybase")) {
147 20 : tds->out_flag = TDS_REPLY;
148 20 : tds_env_change(tds, TDS_ENV_DATABASE, "master", "pubs2");
149 20 : if (IS_TDS71_PLUS(tds->conn))
150 20 : tds_put_n(tds, "\xe3\x08\x00\x07\x05\x09\x04\xd0\x00\x34\x00", 11);
151 20 : tds_send_msg(tds, 5701, 2, 10, "Changed database context to 'pubs2'.", "JDBC", NULL, 1);
152 20 : if (!login->suppress_language) {
153 20 : tds_env_change(tds, TDS_ENV_LANG, NULL, "us_english");
154 20 : tds_send_msg(tds, 5703, 1, 10, "Changed language setting to 'us_english'.", "JDBC", NULL, 1);
155 : }
156 20 : if (IS_TDS50(tds->conn))
157 0 : tds_env_change(tds, TDS_ENV_PACKSIZE, NULL, "512");
158 20 : tds_send_login_ack(tds, "Microsoft SQL Server");
159 20 : if (!IS_TDS50(tds->conn))
160 20 : tds_env_change(tds, TDS_ENV_PACKSIZE, "4096", "4096");
161 20 : if (IS_TDS50(tds->conn))
162 0 : tds_send_capabilities_token(tds);
163 20 : tds_send_done_token(tds, TDS_DONE_FINAL, 0);
164 : } else {
165 0 : exit(1);
166 : }
167 20 : tds_flush_packet(tds);
168 20 : tds_free_login(login);
169 20 : login = NULL;
170 :
171 : for (;;) {
172 : const char *query;
173 :
174 280 : query = tds_get_generic_query(tds);
175 280 : if (!query)
176 : break;
177 260 : printf("query : %s\n", query);
178 :
179 260 : tds->out_flag = TDS_REPLY;
180 :
181 260 : if (strncmp(query, "use tempdb", 10) == 0) {
182 20 : tds_env_change(tds, TDS_ENV_DATABASE, "pubs2", "tempdb");
183 20 : if (IS_TDS71_PLUS(tds->conn))
184 20 : tds_put_n(tds, "\xe3\x08\x00\x07\x05\x09\x04\xd0\x00\x34\x00", 11);
185 20 : tds_send_msg(tds, 5701, 2, 10, "Changed database context to 'tempdb'.", "JDBC", NULL, 1);
186 20 : tds_send_done_token(tds, TDS_DONE_FINAL, 0);
187 20 : tds_flush_packet(tds);
188 20 : continue;
189 : }
190 :
191 240 : if (strncmp(query, "SET TEXTSIZE ", 13) == 0) {
192 20 : tds_send_done_token(tds, TDS_DONE_FINAL, 0);
193 20 : tds_flush_packet(tds);
194 20 : continue;
195 : }
196 :
197 220 : parse_sql(tds, query);
198 220 : continue;
199 : }
200 20 : shutdown(sock, SHUT_RDWR);
201 20 : tds_close_socket(tds);
202 20 : tds_free_socket(tds);
203 20 : tds_free_context(ctx);
204 20 : tds_free_query();
205 20 : }
206 :
207 : static char *
208 1490 : read_line(void *param, char *s, size_t size)
209 : {
210 1490 : const char **const p = (const char **) param;
211 1490 : const char *start = *p, *end;
212 : size_t len;
213 :
214 1490 : if (!*start)
215 : return NULL;
216 1270 : end = strchr(start, '\n');
217 1270 : end = end ? end + 1 : strchr(start, 0);
218 1270 : len = (size_t) (end - start);
219 1270 : if (len >= size) {
220 0 : fprintf(stderr, "Line too long\n");
221 0 : exit(1);
222 : }
223 1270 : memcpy(s, start, len);
224 1270 : s[len] = 0;
225 1270 : *p = start + len;
226 1270 : return s;
227 : }
228 :
229 : static void
230 220 : parse_sql(TDSSOCKET *tds, const char *sql)
231 : {
232 220 : bool cond = true;
233 220 : odbc_parser *parser = odbc_init_parser_func(read_line, &sql);
234 : int done_token;
235 : TDSRESULTINFO *resinfo;
236 : TDSCOLUMN *col;
237 :
238 220 : resinfo = tds_alloc_results(1);
239 220 : if (!resinfo) {
240 0 : fprintf(stderr, "out of memory");
241 0 : exit(1);
242 : }
243 220 : col = resinfo->columns[0];
244 220 : tds_set_column_type(tds->conn, col, SYBVARCHAR);
245 220 : col->column_size = 30;
246 220 : col->on_server.column_size = 30;
247 220 : if (!tds_dstr_copy(&col->column_name, "name"))
248 0 : exit(1);
249 220 : col->column_cur_size = 8;
250 220 : col->column_data = (void *) "row data";
251 :
252 1270 : for (;;) {
253 : char *p;
254 1490 : const char *cmd = odbc_get_cmd_line(parser, &p, &cond);
255 :
256 1490 : if (!cmd)
257 : break;
258 :
259 1270 : if (!strcmp(cmd, "info")) {
260 80 : if (!cond)
261 1270 : continue;
262 :
263 : /* RAISERROR */
264 80 : tds_send_msg(tds, 50000, 1, 5, "information message", "JDBC", NULL, 1);
265 80 : continue;
266 : }
267 :
268 1190 : if (!strcmp(cmd, "error")) {
269 140 : if (!cond)
270 0 : continue;
271 :
272 : /* division by zero */
273 140 : tds_send_err(tds, 8134, 1, 16, "division by zero", "JDBC", NULL, 1);
274 140 : continue;
275 : }
276 :
277 1050 : if (!strcmp(cmd, "rowfmt")) {
278 220 : if (!cond)
279 0 : continue;
280 :
281 220 : tds_send_table_header(tds, resinfo);
282 220 : continue;
283 : }
284 :
285 830 : if (!strcmp(cmd, "row")) {
286 330 : if (!cond)
287 0 : continue;
288 :
289 330 : tds_send_row(tds, resinfo);
290 330 : continue;
291 : }
292 :
293 500 : if (!strcmp(cmd, "quit")) {
294 0 : if (!cond)
295 0 : continue;
296 0 : shutdown(tds->conn->s, SHUT_RDWR);
297 0 : break;
298 : }
299 :
300 500 : if (!strcmp(cmd, "done"))
301 : done_token = TDS_DONE_TOKEN;
302 160 : else if (!strcmp(cmd, "doneinproc"))
303 : done_token = TDS_DONEINPROC_TOKEN;
304 : else
305 0 : done_token = 0;
306 : if (done_token) {
307 : const char *tok;
308 : TDS_SMALLINT flags = TDS_DONE_MORE_RESULTS;
309 : int rows = -1;
310 : bool nocount = false;
311 :
312 1240 : while ((tok = odbc_get_tok(&p)) != NULL) {
313 740 : if (!strcmp(tok, "final"))
314 220 : flags &= ~TDS_DONE_MORE_RESULTS;
315 520 : else if (!strcmp(tok, "error"))
316 80 : flags |= TDS_DONE_ERROR;
317 440 : else if (!strcmp(tok, "nocount"))
318 : nocount = true;
319 : else
320 440 : rows = atoi(tok);
321 : }
322 :
323 500 : if (!cond)
324 0 : continue;
325 :
326 500 : if (rows >= 0)
327 440 : flags |= nocount ? 0 : TDS_DONE_COUNT;
328 : else
329 : rows = 0;
330 500 : tds_send_done(tds, done_token, flags, rows);
331 500 : continue;
332 : }
333 0 : odbc_fatal(parser, ": unknown command '%s'\n", cmd);
334 : }
335 220 : tds_free_results(resinfo);
336 220 : odbc_free_parser(parser);
337 220 : tds_flush_packet(tds);
338 220 : }
339 :
340 : /*
341 : * Fetch tests:
342 : * All reply starts with ROWFMT + ROW, we test behaviour or fetch:
343 : * - is there a row?
344 : * - is there an error or info?
345 : * - is there row count and how much
346 : */
347 : static void
348 110 : test_fetch(const char *replies, const char *expected_no_row, const char *expected_row, int line)
349 : {
350 : char *sql;
351 110 : ODBC_BUF *odbc_buf = NULL;
352 110 : int expected_rows = -1;
353 110 : char state[12] = "No";
354 : SQLLEN char_data_ind;
355 : char char_data[64];
356 :
357 110 : printf("Testing SQLFetch, line %d\n", line);
358 :
359 110 : CHKBindCol(1, SQL_C_CHAR, char_data, sizeof(char_data), &char_data_ind, "S");
360 :
361 110 : sql = odbc_buf_asprintf(&odbc_buf, "rowfmt\nrow\n%s", replies);
362 :
363 110 : CHKExecDirect(T(sql), SQL_NTS, "S");
364 110 : CHKFetch("S");
365 110 : sscanf(expected_no_row, " %10s %d", state, &expected_rows);
366 110 : CHKFetch(state);
367 110 : ODBC_CHECK_ROWS(expected_rows);
368 260 : while (CHKMoreResults("SENo") != SQL_NO_DATA)
369 40 : continue;
370 :
371 110 : sql = odbc_buf_asprintf(&odbc_buf, "rowfmt\nrow\nrow\n%s", replies);
372 :
373 110 : CHKExecDirect(T(sql), SQL_NTS, "S");
374 110 : ODBC_CHECK_ROWS(-1);
375 110 : CHKFetch("S");
376 110 : strcpy(state, "S");
377 110 : expected_rows = -1;
378 110 : sscanf(expected_row, " %10s %d", state, &expected_rows);
379 110 : CHKFetch(state);
380 110 : ODBC_CHECK_ROWS(expected_rows);
381 370 : while (CHKMoreResults("SIENo") != SQL_NO_DATA)
382 150 : continue;
383 :
384 110 : odbc_reset_statement();
385 110 : ODBC_FREE();
386 110 : }
387 :
388 : #define test_fetch(r, e1, e2) test_fetch(r, e1, e2, __LINE__)
389 :
390 10 : TEST_MAIN()
391 : {
392 : int port;
393 : char connect[100];
394 :
395 : tds_socket_init();
396 :
397 10 : for (port = 12340; port < 12350; ++port)
398 10 : if (init_fake_server(port))
399 : break;
400 10 : if (port == 12350) {
401 0 : fprintf(stderr, "Cannot bind to a port\n");
402 0 : return 1;
403 : }
404 10 : printf("Fake server bound at port %d\n", port);
405 :
406 : /* Get SRV from user's PWD config then override UID/PWD/DB */
407 10 : odbc_read_login_info();
408 10 : setup_override();
409 :
410 10 : odbc_use_version3 = true;
411 10 : sprintf(connect, "SERVER=127.0.0.1,%d;TDS_Version=7.3;Encrypt=No;", port);
412 10 : odbc_conn_additional_params = connect;
413 10 : odbc_connect();
414 :
415 : /*
416 : * Test cases for SQLFetch (with and without row)
417 : */
418 :
419 : /* info + done with row */
420 10 : test_fetch("info\ndone 1\ndone final 2", "No 1", "");
421 :
422 : /* info + done with row */
423 10 : if (!odbc_driver_is_freetds())
424 0 : test_fetch("info\ndone error 1\ndone final 2", "No 1", "");
425 :
426 : /* info + done with row */
427 10 : if (!odbc_driver_is_freetds())
428 0 : test_fetch("info\ndone error\ndone final 2", "No 0", "");
429 :
430 : /* error + done with row */
431 10 : test_fetch("error\ndone 1\ndone final", "E 1", "");
432 :
433 : /* error + done with row */
434 10 : test_fetch("error\ndone error 1\ndone final", "E 1", "");
435 :
436 : /* error + done with row */
437 10 : if (!odbc_driver_is_freetds())
438 0 : test_fetch("error\ndone error\ndone final 3", "E 0", "");
439 :
440 : /* info + doneinproc with row */
441 10 : test_fetch("info\ndoneinproc 1\ndone final", "No 1", "");
442 :
443 : /* info + doneinproc with row */
444 10 : if (!odbc_driver_is_freetds())
445 0 : test_fetch("info\ndoneinproc error 1\ndone final", "No 1", "");
446 :
447 : /* info + doneinproc with row */
448 10 : if (!odbc_driver_is_freetds())
449 0 : test_fetch("info\ndoneinproc error\ndone final 3", "No 0", "");
450 :
451 : /* error + doneinproc with row */
452 10 : if (!odbc_driver_is_freetds())
453 0 : test_fetch("error\ndoneinproc 1\ndoneinproc 2\ndone final", "E 1", "");
454 :
455 : /* error + doneinproc with row */
456 10 : if (!odbc_driver_is_freetds())
457 0 : test_fetch("error\ndoneinproc error 1\ndoneinproc 2\ndone final", "E 1", "");
458 :
459 : /* error + doneinproc with row */
460 10 : if (!odbc_driver_is_freetds())
461 0 : test_fetch("error\ndoneinproc error\ndoneinproc 2\ndone final", "E 0", "");
462 :
463 : /* doneinproc with row + doneinproc with different row */
464 10 : if (!odbc_driver_is_freetds())
465 0 : test_fetch("doneinproc 1\ndoneinproc 2\ndone final", "No 1", "S 1");
466 :
467 : /* doneinproc with row + done with different row */
468 10 : if (!odbc_driver_is_freetds())
469 0 : test_fetch("doneinproc 1\ndone 2\ndone final", "No 1", "S 1");
470 :
471 : /* done with row + done with different row */
472 10 : if (!odbc_driver_is_freetds())
473 0 : test_fetch("done 1\ndone 2\ndone final", "No 1", "S 1");
474 :
475 : /* doneinproc without row + doneinproc with rows */
476 10 : if (!odbc_driver_is_freetds())
477 0 : test_fetch("doneinproc\ndoneinproc 2\ndone final 3", "No 0", "S 0");
478 :
479 : /* doneinproc with row but not count flag + doneinproc with rows */
480 10 : if (!odbc_driver_is_freetds())
481 0 : test_fetch("doneinproc nocount 5\ndoneinproc 2\ndone final 3", "No 5", "S 5");
482 :
483 10 : odbc_disconnect();
484 :
485 10 : odbc_use_version3 = false;
486 10 : odbc_connect();
487 :
488 : /*
489 : * Test cases for SQLFetch (with and without row)
490 : */
491 :
492 : /* info + done with row */
493 10 : test_fetch("info\ndone 1\ndone final 2", "No 1", "");
494 :
495 : /* info + done with row */
496 10 : if (!odbc_driver_is_freetds())
497 0 : test_fetch("info\ndone error 1\ndone final 2", "No 1", "");
498 :
499 : /* info + done with row */
500 10 : if (!odbc_driver_is_freetds())
501 0 : test_fetch("info\ndone error\ndone final 2", "No 0", "");
502 :
503 : /* error + done with row */
504 10 : test_fetch("error\ndone 1\ndone final", "E 1", "");
505 :
506 : /* error + done with row */
507 10 : test_fetch("error\ndone error 1\ndone final", "E 1", "");
508 :
509 : /* error + done with row */
510 10 : if (!odbc_driver_is_freetds())
511 0 : test_fetch("error\ndone error\ndone final 3", "E 0", "");
512 :
513 : /* info + doneinproc with row */
514 10 : test_fetch("info\ndoneinproc 1\ndone final", "No 1", "");
515 :
516 : /* info + doneinproc with row */
517 10 : if (!odbc_driver_is_freetds())
518 0 : test_fetch("info\ndoneinproc error 1\ndone final", "No 1", "");
519 :
520 : /* info + doneinproc with row */
521 10 : if (!odbc_driver_is_freetds())
522 0 : test_fetch("info\ndoneinproc error\ndone final 3", "No 0", "");
523 :
524 : /* error + doneinproc with row */
525 10 : test_fetch("error\ndoneinproc 1\ndoneinproc 2\ndone final", "E 2", "");
526 :
527 : /* error + doneinproc with row */
528 10 : test_fetch("error\ndoneinproc error 1\ndoneinproc 2\ndone final", "E 2", "");
529 :
530 : /* error + doneinproc with row */
531 10 : test_fetch("error\ndoneinproc error\ndoneinproc 2\ndone final", "E 2", "");
532 :
533 : /* doneinproc with row + doneinproc with different row */
534 10 : if (!odbc_driver_is_freetds())
535 0 : test_fetch("doneinproc 1\ndoneinproc 2\ndone final", "No 2", "S 2");
536 :
537 : /* doneinproc with row + done with different row */
538 10 : if (!odbc_driver_is_freetds())
539 0 : test_fetch("doneinproc 1\ndone 2\ndone final", "No 1", "S 1");
540 :
541 : /* done with row + done with different row */
542 10 : if (!odbc_driver_is_freetds())
543 0 : test_fetch("done 1\ndone 2\ndone final", "No 1", "S 1");
544 :
545 : /* doneinproc without row + doneinproc with rows */
546 10 : if (!odbc_driver_is_freetds())
547 0 : test_fetch("doneinproc\ndoneinproc 2\ndone final 3", "No 2", "S 2");
548 :
549 10 : odbc_disconnect();
550 :
551 10 : shutdown(stop_socket, SHUT_RDWR);
552 20 : tds_thread_join(fake_thread, NULL);
553 :
554 10 : return 0;
555 : }
556 :
557 : #else /* !TDS_HAVE_MUTEX */
558 : TEST_MAIN()
559 : {
560 : printf("Not possible for this platform.\n");
561 : odbc_test_skipped();
562 : return 0;
563 : }
564 : #endif
|