Line data Source code
1 : /*
2 : * Purpose: Test bcp in and out, and specifically bcp_colfmt()
3 : * Functions: bcp_colfmt bcp_columns bcp_exec bcp_init
4 : */
5 :
6 : #include "common.h"
7 :
8 : #include <freetds/bool.h>
9 : #include <freetds/replacements.h>
10 : #include <freetds/utils.h>
11 :
12 : static bool failed = false;
13 :
14 : static void
15 0 : failure(const char *fmt, ...)
16 : {
17 : va_list ap;
18 :
19 0 : failed = true;
20 :
21 0 : va_start(ap, fmt);
22 0 : vfprintf(stderr, fmt, ap);
23 0 : va_end(ap);
24 0 : }
25 :
26 : #define INFILE_NAME "t0016"
27 : #define TABLE_NAME "#dblib0016"
28 :
29 : static void test_file(const char *fn);
30 : static bool compare_files(const char *fn1, const char *fn2);
31 : static unsigned count_file_rows(FILE *f);
32 : static size_t fgets_raw(char *s, int len, FILE * f);
33 : static DBPROCESS *dbproc;
34 :
35 : /* custom error handler for clean exit */
36 : static int
37 0 : t0016_syb_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)
38 : {
39 0 : int retval = syb_err_logmsg(dbproc, severity, dberr, oserr, dberrstr, oserrstr);
40 :
41 0 : if (retval != 0)
42 : return retval;
43 :
44 0 : failed = true;
45 0 : return INT_CANCEL;
46 : }
47 :
48 10 : TEST_MAIN()
49 : {
50 : LOGINREC *login;
51 : char in_file[30];
52 10 : unsigned int n = 1, end = 100;
53 10 : unsigned int n_pass = 0, n_fail = 0;
54 :
55 10 : setbuf(stdout, NULL);
56 10 : setbuf(stderr, NULL);
57 :
58 10 : set_malloc_options();
59 :
60 10 : read_login_info(argc, argv);
61 10 : printf("Starting %s\n", argv[0]);
62 10 : dbsetversion(DBVERSION_100);
63 10 : dbinit();
64 :
65 10 : dberrhandle(syb_err_handler);
66 10 : dbmsghandle(syb_msg_handler);
67 :
68 10 : printf("About to logon\n");
69 :
70 10 : login = dblogin();
71 10 : BCP_SETL(login, TRUE);
72 10 : DBSETLPWD(login, PASSWORD);
73 10 : DBSETLUSER(login, USER);
74 10 : DBSETLAPP(login, "t0016");
75 10 : DBSETLCHARSET(login, "utf8");
76 :
77 10 : dbproc = dbopen(login, SERVER);
78 10 : if (strlen(DATABASE)) {
79 10 : dbuse(dbproc, DATABASE);
80 : }
81 10 : dbloginfree(login);
82 10 : printf("After logon\n");
83 :
84 : /* First testcase already had SQL opened by read_login_info() */
85 10 : strcpy(in_file, INFILE_NAME);
86 :
87 : /* Developer option to focus on specific tests */
88 : {
89 10 : char const *var = getenv("TDST0016_BEGIN");
90 :
91 10 : if (var)
92 0 : n = atoi(var);
93 10 : var = getenv("TDST0016_END");
94 10 : if (var)
95 0 : end = atoi(var);
96 : }
97 :
98 : /* Execute all testcases (carry on even if one fails) */
99 10 : if (n < 1)
100 0 : n = 1;
101 220 : for (; n <= end; ++n) {
102 230 : failed = FALSE;
103 230 : test_file(in_file);
104 230 : failed ? ++n_fail : ++n_pass;
105 :
106 230 : sprintf(in_file, "%s_%d", INFILE_NAME, n);
107 230 : if (sql_reopen(in_file) != SUCCEED)
108 : break;
109 : }
110 10 : printf("===== End of t0016 subtests =====\n");
111 :
112 10 : dbclose(dbproc);
113 10 : dbexit();
114 :
115 10 : printf("dblib t0016: %d pass, %d fail.\n", n_pass, n_fail);
116 10 : printf("=================================\n");
117 10 : return n_fail ? 1 : 0;
118 : }
119 :
120 : static bool got_error = false;
121 :
122 : static int
123 56 : ignore_msg_handler(DBPROCESS * dbproc TDS_UNUSED, DBINT msgno TDS_UNUSED, int state TDS_UNUSED, int severity TDS_UNUSED,
124 : char *text TDS_UNUSED, char *server TDS_UNUSED, char *proc TDS_UNUSED, int line TDS_UNUSED)
125 : {
126 56 : got_error = true;
127 56 : return 0;
128 : }
129 :
130 : static int
131 56 : ignore_err_handler(DBPROCESS * dbproc TDS_UNUSED, int severity TDS_UNUSED, int dberr TDS_UNUSED,
132 : int oserr TDS_UNUSED, char *dberrstr TDS_UNUSED, char *oserrstr TDS_UNUSED)
133 : {
134 56 : got_error = true;
135 56 : return INT_CANCEL;
136 : }
137 :
138 : /* T0016 data files are all tab-delimited char columns, but we support omitting
139 : * columns in order to simulate testing of BCP with a format file that omits
140 : * columns (feature supported by ASE, but not MSSQL)
141 : */
142 : static void
143 352 : format_columns(int table_cols, char *col_list)
144 : {
145 352 : const char *seps = ", ";
146 352 : int colnum = 1; /* bcp_colfmt uses 1-based indexing */
147 : RETCODE ret;
148 352 : const char *tok = NULL;
149 : int num_cols;
150 :
151 : /* Count number of columns wanted */
152 354 : if (col_list && col_list[0]) {
153 : char *dup;
154 :
155 2 : printf("Custom columns: %s\n", col_list);
156 2 : num_cols = 0;
157 2 : dup = xstrdup(col_list);
158 4 : for (tok = strtok(dup, seps); tok; tok = strtok(NULL, seps))
159 2 : ++num_cols;
160 2 : free(dup);
161 :
162 2 : tok = strtok(col_list, seps);
163 : } else
164 : num_cols = table_cols;
165 :
166 352 : if (num_cols == 0) {
167 0 : failure("PARAM cols contained no valid items.\n");
168 0 : return;
169 : }
170 :
171 352 : ret = bcp_columns(dbproc, num_cols);
172 352 : if (ret != SUCCEED) {
173 0 : failure("return from bcp_columns = %d\n", ret);
174 0 : return;
175 : }
176 :
177 1852 : for (colnum = 1; colnum <= num_cols; ++colnum) {
178 : int host_column;
179 :
180 1852 : if (tok) {
181 2 : host_column = atoi(tok);
182 2 : if (host_column < 1 || host_column > table_cols)
183 0 : failure("PARAM cols %d out of range (1-%d)\n", host_column, table_cols);
184 2 : tok = strtok(NULL, seps);
185 : } else
186 : host_column = colnum;
187 :
188 1852 : ret = bcp_colfmt(dbproc, host_column, SYBCHAR, 0, -1,
189 : (BYTE *) (colnum == num_cols ? "\n" : "\t"), sizeof(char), colnum);
190 :
191 1852 : if (ret == FAIL)
192 0 : failure("return from bcp_colfmt(%d,%d) = FAIL\n", host_column, colnum);
193 : }
194 : }
195 :
196 : static char line1[1024 * 16];
197 : static char line2[1024 * 16];
198 :
199 : static void
200 230 : test_file(const char *fn)
201 : {
202 : RETCODE ret;
203 230 : int num_cols = 0;
204 230 : const char *out_file = "t0016.out";
205 230 : const char *err_file = "t0016.err";
206 : DBINT rows_copied;
207 230 : unsigned num_rows = 2;
208 : char table_name[64];
209 : char hints[64];
210 230 : char colin[64] = { 0 };
211 230 : char colout[64] = { 0 };
212 230 : bool fmt_file_exists = false;
213 :
214 : FILE *input_file;
215 :
216 : char sql_file[256];
217 : char in_file[256];
218 : char exp_file[256];
219 : char fmt_file[256];
220 :
221 230 : snprintf(sql_file, sizeof(sql_file), "%s/%s.sql", FREETDS_SRCDIR, fn);
222 230 : snprintf(in_file, sizeof(in_file), "%s/%s.in", FREETDS_SRCDIR, fn);
223 230 : snprintf(exp_file, sizeof(exp_file), "%s/%s.exp", FREETDS_SRCDIR, fn);
224 230 : snprintf(fmt_file, sizeof(fmt_file), "%s/%s.fmt", FREETDS_SRCDIR, fn);
225 :
226 230 : strlcpy(table_name, TABLE_NAME, sizeof(table_name));
227 230 : hints[0] = 0;
228 :
229 230 : printf("===== %s =====\n", fn);
230 230 : input_file = fopen(fmt_file, "r");
231 230 : if (input_file) {
232 10 : fmt_file_exists = true;
233 10 : fclose(input_file);
234 : }
235 :
236 230 : input_file = fopen(in_file, "r");
237 230 : if (!input_file) {
238 0 : sprintf(in_file, "%s.in", fn);
239 0 : sprintf(exp_file, "%s.exp", fn);
240 0 : input_file = fopen(in_file, "rb");
241 : }
242 230 : if (!input_file) {
243 0 : fprintf(stderr, "could not open %s\n", in_file);
244 0 : exit(1);
245 : }
246 230 : num_rows = count_file_rows(input_file);
247 230 : fclose(input_file);
248 :
249 230 : input_file = fopen(exp_file, "r");
250 230 : if (!input_file)
251 180 : strcpy(exp_file, in_file);
252 : else
253 50 : fclose(input_file);
254 :
255 230 : input_file = fopen(sql_file, "r");
256 230 : assert(input_file);
257 : for (;;) {
258 : const char *param;
259 270 : size_t len = fgets_raw(line1, sizeof(line1), input_file);
260 :
261 270 : if (len && line1[len - 1] == '\n')
262 270 : line1[len - 1] = '\0';
263 270 : if (len == 0 || strncmp(line1, "-- PARAM:", 9) != 0)
264 : break;
265 40 : param = line1 + 9;
266 40 : if (strncmp(param, "table ", 6) == 0) {
267 10 : param += 6;
268 10 : strlcpy(table_name, param, sizeof(table_name));
269 30 : } else if (strncmp(param, "hints ", 6) == 0) {
270 20 : param += 6;
271 20 : strlcpy(hints, param, sizeof(hints));
272 10 : } else if (strncmp(param, "colin ", 6) == 0) {
273 10 : param += 6;
274 10 : strlcpy(colin, param, sizeof(colin));
275 0 : } else if (strncmp(param, "colout ", 7) == 0) {
276 0 : param += 7;
277 0 : strlcpy(colout, param, sizeof(colout));
278 : } else {
279 0 : fprintf(stderr, "invalid parameter: %s\n", param);
280 0 : exit(1);
281 : }
282 : }
283 230 : fclose(input_file);
284 :
285 230 : dberrhandle(ignore_err_handler);
286 230 : dbmsghandle(ignore_msg_handler);
287 :
288 230 : printf("Creating table '%s'\n", table_name);
289 230 : got_error = false;
290 230 : sql_cmd(dbproc);
291 230 : dbsqlexec(dbproc);
292 710 : while (dbresults(dbproc) != NO_MORE_RESULTS)
293 250 : continue;
294 :
295 230 : dberrhandle(t0016_syb_err_handler);
296 230 : dbmsghandle(syb_msg_handler);
297 :
298 230 : if (got_error) {
299 44 : printf("Skipping %s due to table creation failure.\n", fn);
300 44 : return;
301 : }
302 :
303 186 : ret = sql_cmd(dbproc);
304 186 : printf("return from dbcmd = %d\n", ret);
305 :
306 186 : ret = dbsqlexec(dbproc);
307 186 : printf("return from dbsqlexec = %d\n", ret);
308 :
309 186 : if (dbresults(dbproc) != FAIL) {
310 186 : num_cols = dbnumcols(dbproc);
311 186 : printf("Number of columns = %d\n", num_cols);
312 :
313 372 : while (dbnextrow(dbproc) != NO_MORE_ROWS)
314 0 : continue;
315 : }
316 :
317 : /* BCP in */
318 186 : printf("bcp_init with in_file as '%s'\n", in_file);
319 186 : ret = bcp_init(dbproc, table_name, in_file, (char *) err_file, DB_IN);
320 186 : if (ret != SUCCEED)
321 0 : failure("bcp_init failed\n");
322 :
323 186 : if (hints[0])
324 12 : bcp_options(dbproc, BCPHINTS, (BYTE *) hints, (int) strlen(hints));
325 :
326 186 : printf("return from bcp_init = %d\n", ret);
327 :
328 : /* Check for a format file; if not, then format all columns as SYBCHAR tab/newline delimit */
329 186 : if (fmt_file_exists && SUCCEED == bcp_readfmt(dbproc, fmt_file))
330 10 : printf("Loaded %s\n", fmt_file);
331 : else
332 176 : format_columns(num_cols, colin);
333 :
334 186 : ret = bcp_exec(dbproc, &rows_copied);
335 186 : if (ret != SUCCEED || rows_copied != num_rows)
336 0 : failure("bcp_exec failed\n");
337 :
338 186 : printf("%d rows copied in\n", rows_copied);
339 :
340 : /* BCP out */
341 :
342 186 : rows_copied = 0;
343 186 : ret = bcp_init(dbproc, table_name, (char *) out_file, (char *) err_file, DB_OUT);
344 186 : if (ret != SUCCEED)
345 0 : failure("bcp_init failed\n");
346 :
347 186 : printf("select\n");
348 186 : sql_cmd(dbproc);
349 186 : dbsqlexec(dbproc);
350 :
351 186 : if (dbresults(dbproc) != FAIL) {
352 186 : num_cols = dbnumcols(dbproc);
353 372 : while (dbnextrow(dbproc) != NO_MORE_ROWS)
354 0 : continue;
355 : }
356 :
357 186 : if (fmt_file_exists && SUCCEED == bcp_readfmt(dbproc, fmt_file))
358 10 : printf("Loaded %s\n", fmt_file);
359 : else
360 176 : format_columns(num_cols, colout);
361 :
362 186 : ret = bcp_exec(dbproc, &rows_copied);
363 186 : if (ret != SUCCEED || rows_copied != num_rows)
364 0 : failure("bcp_exec failed\n");
365 :
366 186 : printf("%d rows copied out\n", rows_copied);
367 :
368 186 : printf("Dropping table '%s'\n", table_name);
369 186 : sql_cmd(dbproc);
370 186 : dbsqlexec(dbproc);
371 562 : while (dbresults(dbproc) != NO_MORE_RESULTS)
372 190 : continue;
373 :
374 186 : if (failed)
375 : return;
376 :
377 186 : if (compare_files(exp_file, out_file))
378 186 : printf("Input and output files are equal\n");
379 : else
380 0 : failed = true;
381 : }
382 :
383 : static size_t
384 2284 : fgets_raw(char *s, int len, FILE *f)
385 : {
386 2284 : char *p = s;
387 :
388 778854 : while (len > 1) {
389 776570 : int c = getc(f);
390 776570 : if (c == EOF) {
391 602 : if (ferror(f))
392 : return 0;
393 : break;
394 : }
395 775968 : *p++ = c;
396 775968 : --len;
397 775968 : if (c == '\n')
398 : break;
399 : }
400 2284 : if (len > 0)
401 2284 : *p = 0;
402 2284 : return p - s;
403 : }
404 :
405 : static bool
406 186 : compare_files(const char *fn1, const char *fn2)
407 : {
408 186 : bool equal = true;
409 : FILE *f1, *f2;
410 : size_t s1, s2;
411 :
412 : /* check input and output should be the same */
413 186 : f1 = fopen(fn1, "r");
414 186 : f2 = fopen(fn2, "r");
415 186 : if (f1 != NULL && f2 != NULL) {
416 : int line = 1;
417 :
418 436 : for (;; ++line) {
419 1058 : s1 = fgets_raw(line1, sizeof(line1), f1);
420 622 : s2 = fgets_raw(line2, sizeof(line2), f2);
421 :
422 : /* EOF or error of one */
423 622 : if (!!s1 != !!s2) {
424 0 : equal = false;
425 0 : failure("error reading a file or EOF of a file\n");
426 0 : break;
427 : }
428 :
429 : /* EOF or error of both */
430 622 : if (!s1) {
431 186 : if (feof(f1) && feof(f2))
432 : break;
433 0 : equal = false;
434 0 : failure("error reading a file\n");
435 0 : break;
436 : }
437 :
438 436 : if (s1 != s2 || memcmp(line1, line2, s1) != 0) {
439 0 : equal = false;
440 0 : failure("File different at line %d\n"
441 : " %s: %s"
442 0 : " output: %s%s", line, fn1[strlen(fn1) - 1] == 'p' ? "expect" : " input", line1, line2,
443 : /* don't double-newline if expected output had one already */
444 0 : (line2[0] && line2[strlen(line2) - 1] == '\n') ? "" : "\n");
445 :
446 : }
447 : }
448 : } else {
449 0 : equal = false;
450 0 : failure("error opening files\n");
451 : }
452 186 : if (f1)
453 186 : fclose(f1);
454 186 : if (f2)
455 186 : fclose(f2);
456 :
457 186 : return equal;
458 : }
459 :
460 : static unsigned
461 230 : count_file_rows(FILE *f)
462 : {
463 : size_t s;
464 230 : unsigned rows = 1;
465 230 : char last = '\n';
466 :
467 230 : assert(f);
468 :
469 770 : while ((s = fgets_raw(line1, sizeof(line1), f)) != 0) {
470 540 : last = line1[s-1];
471 540 : if (last == '\n')
472 540 : ++rows;
473 : }
474 230 : if (last == '\n')
475 230 : --rows;
476 230 : assert(!ferror(f));
477 230 : return rows;
478 : }
|