LCOV - code coverage report
Current view: top level - src/dblib/unittests - t0016.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 212 261 81.2 %
Date: 2026-07-19 14:02:14 Functions: 8 10 80.0 %

          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             : }

Generated by: LCOV version 1.13