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