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