LCOV - code coverage report
Current view: top level - src/dblib/unittests - t0016.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 193 232 83.2 %
Date: 2026-05-04 13:14:38 Functions: 8 9 88.9 %

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

Generated by: LCOV version 1.13