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