LCOV - code coverage report
Current view: top level - src/ctlib/unittests - blk_in.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 163 202 80.7 %
Date: 2025-07-02 09:40:27 Functions: 11 11 100.0 %

          Line data    Source code
       1             : #include "common.h"
       2             : 
       3             : #if HAVE_SYS_STAT_H
       4             : #include <sys/stat.h>
       5             : #endif /* HAVE_SYS_STAT_H */
       6             : 
       7             : #include <bkpublic.h>
       8             : 
       9             : #include <freetds/replacements.h>
      10             : 
      11             : static void
      12             : do_bind(CS_BLKDESC * blkdesc, int colnum, CS_INT host_format, CS_INT host_type, CS_INT host_maxlen,
      13             :         void        *var_addr,
      14             :         CS_INT      *var_len_addr,
      15             :         CS_SMALLINT *var_ind_addr );
      16             : static void do_one_bind(CS_BLKDESC * blkdesc, int col, const char *name);
      17             : static FILE *open_test_file(const char *filename);
      18             : typedef enum
      19             : {
      20             :         PART_END,
      21             :         PART_SQL,
      22             :         PART_BIND,
      23             :         PART_OUTPUT,
      24             : } part_t;
      25             : static part_t read_part_type(FILE * in);
      26             : static char *read_part(FILE * in);
      27             : static void read_line(char *buf, size_t buf_len, FILE * f);
      28             : static char *append_string(char *s1, const char *s2);
      29             : static char *get_output(CS_COMMAND * cmd);
      30             : static void single_test(CS_CONNECTION * conn, CS_COMMAND * cmd, FILE * in);
      31             : 
      32             : /*
      33             :  * Static data for insertion
      34             :  */
      35             : static int  not_null_bit = 1;
      36             : static CS_INT      l_not_null_bit = 4;
      37             : static CS_SMALLINT i_not_null_bit = 0;
      38             : 
      39             : static char not_null_char[] = "a char";
      40             : static CS_INT      l_not_null_char = 6;
      41             : static CS_SMALLINT i_not_null_char = 0;
      42             : 
      43             : static char not_null_varchar[] = "a varchar";
      44             : static CS_INT      l_not_null_varchar = 9;
      45             : static CS_SMALLINT i_not_null_varchar = 0;
      46             : 
      47             : static char not_null_datetime[] = "Dec 17 2003  3:44PM";
      48             : static CS_INT      l_not_null_datetime = 19;
      49             : static CS_SMALLINT i_not_null_datetime = 0;
      50             : 
      51             : static char not_null_smalldatetime[] = "Dec 17 2003  3:44PM";
      52             : static CS_INT      l_not_null_smalldatetime = 19;
      53             : static CS_SMALLINT i_not_null_smalldatetime = 0;
      54             : 
      55             : static char not_null_money[] = "12.34";
      56             : static CS_INT      l_not_null_money = 5;
      57             : static CS_SMALLINT i_not_null_money = 0;
      58             : 
      59             : static char not_null_smallmoney[] = "12.34";
      60             : static CS_INT      l_not_null_smallmoney = 5;
      61             : static CS_SMALLINT i_not_null_smallmoney = 0;
      62             : 
      63             : static char not_null_float[] = "12.34";
      64             : static CS_INT      l_not_null_float = 5;
      65             : static CS_SMALLINT i_not_null_float = 0;
      66             : 
      67             : static char not_null_real[] = "12.34";
      68             : static CS_INT      l_not_null_real = 5;
      69             : static CS_SMALLINT i_not_null_real = 0;
      70             : 
      71             : static char not_null_decimal[] = "12.34";
      72             : static CS_INT      l_not_null_decimal = 5;
      73             : static CS_SMALLINT i_not_null_decimal = 0;
      74             : 
      75             : static char not_null_numeric[] = "12.34";
      76             : static CS_INT      l_not_null_numeric = 5;
      77             : static CS_SMALLINT i_not_null_numeric = 0;
      78             : 
      79             : static int  not_null_int        = 1234;
      80             : static CS_INT      l_not_null_int = 4;
      81             : static CS_SMALLINT i_not_null_int = 0;
      82             : 
      83             : static int  not_null_smallint   = 1234;
      84             : static CS_INT      l_not_null_smallint = 4;
      85             : static CS_SMALLINT i_not_null_smallint = 0;
      86             : 
      87             : static int  not_null_tinyint    = 123;
      88             : static CS_INT      l_not_null_tinyint = 4;
      89             : static CS_SMALLINT i_not_null_tinyint = 0;
      90             : 
      91             : static CS_INT      l_null_char = 0;
      92             : static CS_SMALLINT i_null_char = -1;
      93             : 
      94             : static CS_INT      l_null_varchar = 0;
      95             : static CS_SMALLINT i_null_varchar = -1;
      96             : 
      97             : static CS_INT      l_null_datetime = 0;
      98             : static CS_SMALLINT i_null_datetime = -1;
      99             : 
     100             : static CS_INT      l_null_smalldatetime = 0;
     101             : static CS_SMALLINT i_null_smalldatetime = -1;
     102             : 
     103             : static CS_INT      l_null_money = 0;
     104             : static CS_SMALLINT i_null_money = -1;
     105             : 
     106             : static CS_INT      l_null_smallmoney = 0;
     107             : static CS_SMALLINT i_null_smallmoney = -1;
     108             : 
     109             : static CS_INT      l_null_float = 0;
     110             : static CS_SMALLINT i_null_float = -1;
     111             : 
     112             : static CS_INT      l_null_real = 0;
     113             : static CS_SMALLINT i_null_real = -1;
     114             : 
     115             : static CS_INT      l_null_decimal = 0;
     116             : static CS_SMALLINT i_null_decimal = -1;
     117             : 
     118             : static CS_INT      l_null_numeric = 0;
     119             : static CS_SMALLINT i_null_numeric = -1;
     120             : 
     121             : static CS_INT      l_null_int = 0;
     122             : static CS_SMALLINT i_null_int = -1;
     123             : 
     124             : static CS_INT      l_null_smallint = 0;
     125             : static CS_SMALLINT i_null_smallint = -1;
     126             : 
     127             : static CS_INT      l_null_tinyint = 0;
     128             : static CS_SMALLINT i_null_tinyint = -1;
     129             : 
     130             : static char not_null_varbinary[] = "123456789";
     131             : static CS_INT      l_not_null_varbinary = 9;
     132             : static CS_SMALLINT i_not_null_varbinary = 0;
     133             : 
     134             : static void
     135          22 : do_binds(CS_BLKDESC *blkdesc, FILE *in)
     136             : {
     137             :         char line[1024];
     138          22 :         int col = 1;
     139             : 
     140             :         for (;;) {
     141         690 :                 read_line(line, sizeof(line), in);
     142         356 :                 if (strcmp(line, "--\n") == 0)
     143          22 :                         return;
     144         334 :                 strtok(line, "\n");
     145         334 :                 do_one_bind(blkdesc, col, line);
     146         334 :                 ++col;
     147             :         }
     148             : }
     149             : 
     150             : static void
     151         334 : do_one_bind(CS_BLKDESC *blkdesc, int col, const char *name)
     152             : {
     153             : #define do_bind(bind_name, fmt, type, len, value) do { \
     154             :         if (strcmp(#bind_name, name) == 0) { \
     155             :                 do_bind(blkdesc, col, fmt, type, len, value, &l_ ## bind_name, &i_ ## bind_name); \
     156             :                 return; \
     157             :         } \
     158             : } while(0)
     159             : 
     160             :         /* non nulls */
     161             : 
     162         334 :         do_bind(not_null_bit, CS_FMT_UNUSED, CS_INT_TYPE, 4, &not_null_bit);
     163         322 :         do_bind(not_null_char, CS_FMT_NULLTERM, CS_CHAR_TYPE, 7, not_null_char);
     164         310 :         do_bind(not_null_varchar, CS_FMT_NULLTERM, CS_CHAR_TYPE, 10, not_null_varchar);
     165         298 :         do_bind(not_null_datetime, CS_FMT_NULLTERM, CS_CHAR_TYPE, 20, not_null_datetime);
     166         286 :         do_bind(not_null_smalldatetime, CS_FMT_NULLTERM, CS_CHAR_TYPE, 20, not_null_smalldatetime);
     167         274 :         do_bind(not_null_money, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_money);
     168         262 :         do_bind(not_null_smallmoney, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_smallmoney);
     169         250 :         do_bind(not_null_float, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_float);
     170         238 :         do_bind(not_null_real, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_real);
     171         226 :         do_bind(not_null_decimal, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_decimal);
     172         214 :         do_bind(not_null_numeric, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_numeric);
     173         202 :         do_bind(not_null_int, CS_FMT_UNUSED, CS_INT_TYPE, 4, &not_null_int);
     174         190 :         do_bind(not_null_smallint, CS_FMT_UNUSED, CS_INT_TYPE, 4, &not_null_smallint);
     175         178 :         do_bind(not_null_tinyint, CS_FMT_UNUSED, CS_INT_TYPE, 4, &not_null_tinyint);
     176         166 :         do_bind(not_null_varbinary, CS_FMT_NULLTERM, CS_BINARY_TYPE, 10, not_null_varbinary);
     177             : 
     178             :         /* nulls */
     179             : 
     180         156 :         do_bind(null_char, CS_FMT_NULLTERM, CS_CHAR_TYPE, 7, not_null_char);
     181         144 :         do_bind(null_varchar, CS_FMT_NULLTERM, CS_CHAR_TYPE, 10, not_null_varchar);
     182         132 :         do_bind(null_datetime, CS_FMT_NULLTERM, CS_CHAR_TYPE, 20, not_null_datetime);
     183         120 :         do_bind(null_smalldatetime, CS_FMT_NULLTERM, CS_CHAR_TYPE, 20, not_null_smalldatetime);
     184         108 :         do_bind(null_money, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_money);
     185          96 :         do_bind(null_smallmoney, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_smallmoney);
     186          84 :         do_bind(null_float, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_float);
     187          72 :         do_bind(null_real, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_real);
     188          60 :         do_bind(null_decimal, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_decimal);
     189          48 :         do_bind(null_numeric, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_numeric);
     190          36 :         do_bind(null_int, CS_FMT_UNUSED, CS_INT_TYPE, 4, &not_null_int);
     191          24 :         do_bind(null_smallint, CS_FMT_UNUSED, CS_INT_TYPE, 4, &not_null_smallint);
     192          12 :         do_bind(null_tinyint, CS_FMT_UNUSED, CS_INT_TYPE, 4, &not_null_tinyint);
     193             : #undef do_bind
     194             : 
     195           0 :         fprintf(stderr, "Column %s not found\n", name);
     196           0 :         exit(1);
     197             : }
     198             : 
     199             : static void
     200         334 : do_bind(CS_BLKDESC * blkdesc, int colnum, CS_INT host_format, CS_INT host_type, CS_INT host_maxlen,
     201             :         void        *var_addr,
     202             :         CS_INT      *var_len_addr,
     203             :         CS_SMALLINT *var_ind_addr )
     204             : {
     205             :         CS_DATAFMT datafmt;
     206             : 
     207         334 :         check_call(blk_describe, (blkdesc, colnum, &datafmt));
     208             : 
     209         334 :         datafmt.format = host_format;
     210         334 :         datafmt.datatype = host_type;
     211         334 :         datafmt.maxlength = host_maxlen;
     212         334 :         datafmt.count = 1;
     213             : 
     214         334 :         check_call(blk_bind, (blkdesc, colnum, &datafmt, var_addr, var_len_addr, var_ind_addr ));
     215         334 : }
     216             : 
     217             : static const char table_name[] = "all_types_bcp_unittest";
     218             : 
     219          10 : TEST_MAIN()
     220             : {
     221             :         CS_CONTEXT *ctx;
     222             :         CS_CONNECTION *conn;
     223             :         CS_COMMAND *cmd;
     224          10 :         int verbose = 0;
     225             :         FILE *in;
     226             :         part_t part;
     227             : 
     228          10 :         printf("%s: Retrieve data using array binding \n", __FILE__);
     229             :         if (verbose) {
     230             :                 printf("Trying login\n");
     231             :         }
     232          10 :         in = open_test_file(argc > 1 ? argv[1] : NULL);
     233          10 :         check_call(try_ctlogin, (&ctx, &conn, &cmd, verbose));
     234             : 
     235             :         for (;;) {
     236          70 :                 part = read_part_type(in);
     237          40 :                 if (part == PART_END)
     238             :                         break;
     239          30 :                 assert(part == PART_SQL);
     240             : 
     241          30 :                 single_test(conn, cmd, in);
     242             :         }
     243             : 
     244          10 :         printf("done\n");
     245             : 
     246          10 :         check_call(try_ctlogout, (ctx, conn, cmd, verbose));
     247          10 :         fclose(in);
     248             : 
     249          10 :         return 0;
     250             : }
     251             : 
     252             : static void
     253          30 : single_test(CS_CONNECTION *conn, CS_COMMAND *cmd, FILE *in)
     254             : {
     255             :         char command[512];
     256             :         char *create_table_sql, *out1, *out2;
     257             :         CS_BLKDESC *blkdesc;
     258          30 :         int count = 0;
     259             :         int i;
     260             :         part_t part;
     261             :         CS_RETCODE ret;
     262             : 
     263          30 :         sprintf(command, "if exists (select 1 from sysobjects where type = 'U' and name = '%s') drop table %s",
     264             :                 table_name, table_name);
     265             : 
     266          30 :         check_call(run_command, (cmd, command));
     267             : 
     268          30 :         create_table_sql = read_part(in);
     269          30 :         ret = run_command(cmd, create_table_sql);
     270          30 :         free(create_table_sql);
     271             : 
     272             :         /* on error skip the test */
     273          30 :         if (ret != CS_SUCCEED) {
     274           8 :                 part = read_part_type(in);
     275           8 :                 assert(part == PART_BIND);
     276           8 :                 free(read_part(in));
     277             : 
     278           8 :                 part = read_part_type(in);
     279           8 :                 assert(part == PART_OUTPUT);
     280           8 :                 free(read_part(in));
     281           8 :                 return;
     282             :         }
     283             : 
     284          22 :         sprintf(command, "delete from %s", table_name);
     285          22 :         check_call(run_command, (cmd, command));
     286             : 
     287          22 :         check_call(blk_alloc, (conn, BLK_VERSION_100, &blkdesc));
     288             : 
     289          22 :         check_call(blk_init, (blkdesc, CS_BLK_IN, (char *) table_name, CS_NULLTERM));
     290             : 
     291          22 :         part = read_part_type(in);
     292          22 :         assert(part == PART_BIND);
     293             : 
     294          22 :         do_binds(blkdesc, in);
     295             : 
     296          22 :         part = read_part_type(in);
     297          22 :         assert(part == PART_OUTPUT);
     298             : 
     299          22 :         printf("Sending same row 10 times... \n");
     300         242 :         for (i = 0; i < 10; i++) {
     301         220 :                 check_call(blk_rowxfer, (blkdesc));
     302             :         }
     303             : 
     304          22 :         check_call(blk_done, (blkdesc, CS_BLK_ALL, &count));
     305             : 
     306          22 :         blk_drop(blkdesc);
     307             : 
     308          22 :         printf("%d rows copied.\n", count);
     309             : 
     310          22 :         out1 = read_part(in);
     311          22 :         out2 = get_output(cmd);
     312          22 :         if (strcmp(out1, out2) != 0) {
     313           0 :                 fprintf(stderr, "Wrong output\n-- expected --\n%s\n-- got --\n%s\n--\n", out1, out2);
     314           0 :                 exit(1);
     315             :         }
     316          22 :         free(out1);
     317          22 :         free(out2);
     318             : }
     319             : 
     320             : static char *
     321          22 : get_output(CS_COMMAND *cmd)
     322             : {
     323             :         char command[512];
     324             : 
     325             :         CS_RETCODE ret;
     326             :         CS_RETCODE results_ret;
     327             :         CS_DATAFMT datafmt;
     328             :         CS_INT datalength;
     329          22 :         CS_SMALLINT *inds = NULL;
     330          22 :         CS_INT count, row_count = 0;
     331             :         CS_INT result_type;
     332          22 :         CS_CHAR *data = NULL;
     333             :         CS_INT num_cols;
     334             :         CS_INT i;
     335          22 :         char *out = strdup("");
     336             : 
     337          22 :         assert(out != NULL);
     338             : 
     339          22 :         sprintf(command, "select distinct * from %s", table_name);
     340          22 :         check_call(ct_command, (cmd, CS_LANG_CMD, command, CS_NULLTERM, CS_UNUSED));
     341             : 
     342          22 :         check_call(ct_send, (cmd));
     343          22 :         while ((results_ret = ct_results(cmd, &result_type)) == CS_SUCCEED) {
     344          44 :                 switch ((int) result_type) {
     345             :                 case CS_CMD_SUCCEED:
     346             :                         break;
     347             :                 case CS_CMD_DONE:
     348             :                         break;
     349           0 :                 case CS_CMD_FAIL:
     350           0 :                         fprintf(stderr, "ct_results() result_type CS_CMD_FAIL.\n");
     351           0 :                         exit(1);
     352          22 :                 case CS_ROW_RESULT:
     353          22 :                         check_call(ct_res_info, (cmd, CS_NUMDATA, &num_cols, CS_UNUSED, NULL));
     354          22 :                         data = malloc(num_cols * 256);
     355          22 :                         assert(data != NULL);
     356          22 :                         inds = calloc(num_cols, sizeof(*inds));
     357          22 :                         assert(inds != NULL);
     358         334 :                         for (i = 0; i < num_cols; i++) {
     359         334 :                                 datafmt.datatype = CS_CHAR_TYPE;
     360         334 :                                 datafmt.format = CS_FMT_NULLTERM;
     361         334 :                                 datafmt.maxlength = 256;
     362         334 :                                 datafmt.count = 1;
     363         334 :                                 datafmt.locale = NULL;
     364         334 :                                 check_call(ct_bind, (cmd, i + 1, &datafmt, data + 256 * i, &datalength, &inds[i]));
     365             :                         }
     366             : 
     367          44 :                         while ((ret = ct_fetch(cmd, CS_UNUSED, CS_UNUSED, CS_UNUSED, &count)) == CS_SUCCEED) {
     368          22 :                                 row_count += count;
     369         356 :                                 for (i = 0; i < num_cols; i++) {
     370         334 :                                         if (!inds[i])
     371         178 :                                                 out = append_string(out, data + 256 * i);
     372             :                                         else
     373         156 :                                                 out = append_string(out, "NULL");
     374         334 :                                         out = append_string(out, "\n");
     375             :                                 }
     376             :                         }
     377          22 :                         switch ((int) ret) {
     378             :                         case CS_END_DATA:
     379             :                                 break;
     380           0 :                         case CS_FAIL:
     381           0 :                                 fprintf(stderr, "ct_fetch() returned CS_FAIL.\n");
     382           0 :                                 exit(1);
     383           0 :                         case CS_ROW_FAIL:
     384           0 :                                 fprintf(stderr, "ct_fetch() CS_ROW_FAIL on row %d.\n", row_count);
     385           0 :                                 exit(1);
     386           0 :                         default:
     387           0 :                                 fprintf(stderr, "ct_fetch() unexpected return.\n");
     388           0 :                                 exit(1);
     389             :                         }
     390             :                         break;
     391           0 :                 case CS_COMPUTE_RESULT:
     392           0 :                         fprintf(stderr, "ct_results() unexpected CS_COMPUTE_RESULT.\n");
     393           0 :                         exit(1);
     394           0 :                 default:
     395           0 :                         fprintf(stderr, "ct_results() unexpected result_type.\n");
     396           0 :                         exit(1);
     397             :                 }
     398             :         }
     399          22 :         switch ((int) results_ret) {
     400             :         case CS_END_RESULTS:
     401             :                 break;
     402           0 :         case CS_FAIL:
     403           0 :                 fprintf(stderr, "ct_results() failed.\n");
     404           0 :                 exit(1);
     405             :                 break;
     406           0 :         default:
     407           0 :                 fprintf(stderr, "ct_results() unexpected return.\n");
     408           0 :                 exit(1);
     409             :         }
     410             : 
     411          22 :         free(data);
     412          22 :         free(inds);
     413          22 :         return out;
     414             : }
     415             : 
     416             : 
     417             : static FILE *
     418          10 : open_test_file(const char *filename)
     419             : {
     420          10 :         FILE *input_file = NULL;
     421             :         char in_file[256];
     422             : 
     423             :         /* If no filename requested, try blk_in.in in both the expected location and the current directory. */
     424          10 :         if (filename)
     425           0 :                 input_file = fopen(filename, "r");
     426             :         else {
     427          10 :                 snprintf(in_file, sizeof(in_file), "%s/blk_in.in", FREETDS_SRCDIR);
     428          10 :                 filename = in_file;
     429          10 :                 input_file = fopen(filename, "r");
     430          10 :                 if (!input_file) {
     431           0 :                         filename = "blk_in.in";
     432           0 :                         input_file = fopen(filename, "r");
     433             :                 }
     434             :         }
     435          10 :         if (!input_file) {
     436           0 :                 fprintf(stderr, "could not open %s\n", filename);
     437           0 :                 exit(1);
     438             :         }
     439          10 :         return input_file;
     440             : }
     441             : 
     442             : static part_t
     443         100 : read_part_type(FILE *in)
     444             : {
     445             :         char line[1024];
     446             :         part_t part;
     447             : 
     448         100 :         read_line(line, sizeof(line), in);
     449         100 :         if (strncmp(line, "end", 3) == 0)
     450             :                 return PART_END;
     451             : 
     452          90 :         if (strcmp(line, "sql\n") == 0)
     453             :                 part = PART_SQL;
     454          60 :         else if (strcmp(line, "bind\n") == 0)
     455             :                 part = PART_BIND;
     456          30 :         else if (strcmp(line, "output\n") == 0)
     457             :                 part = PART_OUTPUT;
     458             :         else {
     459           0 :                 fprintf(stderr, "Invalid part: %s\n", line);
     460           0 :                 exit(1);
     461             :         }
     462          90 :         read_line(line, sizeof(line), in);
     463          90 :         if (strcmp(line, "--\n") != 0) {
     464           0 :                 fprintf(stderr, "Error reading line\n");
     465           0 :                 exit(1);
     466             :         }
     467             :         return part;
     468             : }
     469             : 
     470             : static char *
     471          68 : read_part(FILE *in)
     472             : {
     473             :         char line[1024];
     474          68 :         char *part = strdup("");
     475             : 
     476          68 :         assert(part != NULL);
     477             :         for (;;) {
     478        3586 :                 read_line(line, sizeof(line), in);
     479        3654 :                 if (strcmp(line, "--\n") == 0)
     480          68 :                         return part;
     481        3586 :                 part = append_string(part, line);
     482             :         }
     483             : }
     484             : 
     485             : static void
     486        4200 : read_line(char *buf, size_t buf_len, FILE *f)
     487             : {
     488        4200 :         if (fgets(buf, buf_len, f) == NULL || ferror(f)) {
     489           0 :                 fprintf(stderr, "Error reading line\n");
     490           0 :                 exit(1);
     491             :         }
     492        4200 : }
     493             : 
     494             : static char *
     495        4254 : append_string(char *s1, const char *s2)
     496             : {
     497        4254 :         assert(s1);
     498        4254 :         assert(s2);
     499        4254 :         s1 = realloc(s1, strlen(s1) + strlen(s2) + 1);
     500        4254 :         assert(s1 != NULL);
     501        4254 :         strcat(s1, s2);
     502        4254 :         return s1;
     503             : }

Generated by: LCOV version 1.13