LCOV - code coverage report
Current view: top level - src/ctlib/unittests - blk_cancel.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 116 123 94.3 %
Date: 2026-11-07 23:58:45 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Test bulk copy cancellation (blk_done with CS_BLK_CANCEL).
       3             :  * Check data not committed are discarded and that connection
       4             :  * can be used after cancellation.
       5             :  */
       6             : 
       7             : #include "common.h"
       8             : 
       9             : #include <bkpublic.h>
      10             : 
      11             : static const char table_name[] = "blk_cancel_test";
      12             : 
      13             : static CS_CONNECTION *conn;
      14             : static CS_COMMAND *cmd;
      15             : static CS_BLKDESC *blkdesc;
      16             : 
      17             : static CS_INT row_id, row_id_len = sizeof(row_id);
      18             : static char row_data[201];
      19             : static CS_INT row_data_len;
      20             : 
      21             : /* start a bulk copy into the table binding the columns */
      22             : static void
      23          70 : start_in(void)
      24             : {
      25             :         CS_DATAFMT fmt;
      26             : 
      27          70 :         check_call(blk_init, (blkdesc, CS_BLK_IN, (char *) table_name, CS_NULLTERM));
      28             : 
      29          70 :         memset(&fmt, 0, sizeof(fmt));
      30          70 :         fmt.count = 1;
      31          70 :         fmt.datatype = CS_INT_TYPE;
      32          70 :         fmt.maxlength = sizeof(row_id);
      33          70 :         check_call(blk_bind, (blkdesc, 1, &fmt, &row_id, &row_id_len, NULL));
      34             : 
      35          70 :         memset(&fmt, 0, sizeof(fmt));
      36          70 :         fmt.count = 1;
      37             :         fmt.datatype = CS_CHAR_TYPE;
      38          70 :         fmt.maxlength = sizeof(row_data) - 1;
      39          70 :         check_call(blk_bind, (blkdesc, 2, &fmt, row_data, &row_data_len, NULL));
      40          70 : }
      41             : 
      42             : /* send some rows, enough rows cause packets to be sent to the server */
      43             : static void
      44          70 : send_rows(int first, int num)
      45             : {
      46             :         int i;
      47             : 
      48       60370 :         for (i = 0; i < num; i++) {
      49       60300 :                 row_id = first + i;
      50       60300 :                 row_data_len = 50 + row_id % 150;
      51       60300 :                 memset(row_data, 'a' + row_id % 26, row_data_len);
      52       60300 :                 check_call(blk_rowxfer, (blkdesc));
      53             :         }
      54          70 : }
      55             : 
      56             : static void
      57         110 : done(CS_INT type, CS_INT expected)
      58             : {
      59         110 :         CS_INT rows = -1;
      60             : 
      61         110 :         check_call(blk_done, (blkdesc, type, &rows));
      62         110 :         if (rows != expected) {
      63           0 :                 fprintf(stderr, "blk_done(%d) returned %d rows, expected %d\n", (int) type, (int) rows, (int) expected);
      64           0 :                 exit(1);
      65             :         }
      66         110 : }
      67             : 
      68             : /* check table content, this also check the connection is still usable */
      69             : static void
      70          80 : check_table(CS_INT expected_count, CS_INT expected_sum)
      71             : {
      72             :         char sql[128];
      73             :         CS_DATAFMT fmt;
      74          80 :         CS_INT values[2], result_type, count, num_rows = 0;
      75             :         CS_RETCODE ret;
      76             :         int i;
      77             : 
      78          80 :         sprintf(sql, "select count(*), coalesce(sum(id), 0) from %s", table_name);
      79          80 :         check_call(ct_command, (cmd, CS_LANG_CMD, sql, CS_NULLTERM, CS_UNUSED));
      80          80 :         check_call(ct_send, (cmd));
      81          80 :         while ((ret = ct_results(cmd, &result_type)) == CS_SUCCEED) {
      82         160 :                 switch (result_type) {
      83             :                 case CS_ROW_RESULT:
      84         160 :                         for (i = 0; i < 2; i++) {
      85         160 :                                 memset(&fmt, 0, sizeof(fmt));
      86         160 :                                 fmt.datatype = CS_INT_TYPE;
      87         160 :                                 fmt.maxlength = sizeof(CS_INT);
      88         160 :                                 fmt.count = 1;
      89         160 :                                 check_call(ct_bind, (cmd, i + 1, &fmt, &values[i], NULL, NULL));
      90             :                         }
      91         160 :                         while ((ret = ct_fetch(cmd, CS_UNUSED, CS_UNUSED, CS_UNUSED, &count)) == CS_SUCCEED)
      92          80 :                                 ++num_rows;
      93          80 :                         assert(ret == CS_END_DATA);
      94             :                         break;
      95             :                 case CS_CMD_SUCCEED:
      96             :                 case CS_CMD_DONE:
      97             :                         break;
      98           0 :                 default:
      99           0 :                         fprintf(stderr, "unexpected result type %d\n", (int) result_type);
     100           0 :                         exit(1);
     101             :                 }
     102             :         }
     103          80 :         assert(ret == CS_END_RESULTS);
     104          80 :         assert(num_rows == 1);
     105             : 
     106          80 :         printf("table has %d rows, sum %d\n", (int) values[0], (int) values[1]);
     107          80 :         if (values[0] != expected_count || values[1] != expected_sum) {
     108           0 :                 fprintf(stderr, "wrong table content, expected %d rows, sum %d\n", (int) expected_count, (int) expected_sum);
     109           0 :                 exit(1);
     110             :         }
     111          80 : }
     112             : 
     113             : static void
     114          20 : clear_table(void)
     115             : {
     116             :         char sql[128];
     117             : 
     118          20 :         sprintf(sql, "delete from %s", table_name);
     119          20 :         check_call(run_command, (cmd, sql));
     120          20 : }
     121             : 
     122             : /* sum of ids from first to first + num - 1 */
     123             : static CS_INT
     124             : sum_ids(int first, int num)
     125             : {
     126             :         return (CS_INT) (((long) first * 2 + num - 1) * num / 2);
     127             : }
     128             : 
     129          10 : TEST_MAIN()
     130             : {
     131             :         CS_CONTEXT *ctx;
     132          10 :         bool verbose = false;
     133             :         char sql[256];
     134             :         CS_DATAFMT fmt;
     135             :         CS_INT id, id_len, rows;
     136             :         int i;
     137             : 
     138          10 :         printf("%s: Testing bulk copy cancellation\n", __FILE__);
     139          10 :         check_call(try_ctlogin, (&ctx, &conn, &cmd, verbose));
     140             : 
     141          10 :         sprintf(sql, "if exists (select 1 from sysobjects where type = 'U' and name = '%s') drop table %s", table_name, table_name);
     142          10 :         check_call(run_command, (cmd, sql));
     143             : 
     144          10 :         sprintf(sql, "create table %s (id int not null, data varchar(200) not null)", table_name);
     145          10 :         check_call(run_command, (cmd, sql));
     146             : 
     147          10 :         check_call(blk_alloc, (conn, BLK_VERSION_100, &blkdesc));
     148             : 
     149          10 :         printf("cancel with data not sent yet to server\n");
     150          10 :         start_in();
     151          10 :         send_rows(1, 5);
     152          10 :         done(CS_BLK_CANCEL, 0);
     153          10 :         check_table(0, 0);
     154             : 
     155          10 :         printf("cancel after some packets were sent to server\n");
     156          10 :         start_in();
     157          10 :         send_rows(1, 2000);
     158          10 :         done(CS_BLK_CANCEL, 0);
     159          10 :         check_table(0, 0);
     160             : 
     161          10 :         printf("cancel after a committed batch\n");
     162          10 :         start_in();
     163          10 :         send_rows(1, 10);
     164          10 :         done(CS_BLK_BATCH, 10);
     165          10 :         send_rows(11, 2000);
     166          10 :         done(CS_BLK_CANCEL, 0);
     167          10 :         check_table(10, sum_ids(1, 10));
     168          10 :         clear_table();
     169             : 
     170          10 :         printf("cancel just after a batch\n");
     171          10 :         start_in();
     172          10 :         send_rows(1, 10);
     173          10 :         done(CS_BLK_BATCH, 10);
     174          10 :         done(CS_BLK_CANCEL, 0);
     175          10 :         check_table(10, sum_ids(1, 10));
     176          10 :         clear_table();
     177             : 
     178          10 :         printf("cancel before transferring any row\n");
     179          10 :         start_in();
     180          10 :         done(CS_BLK_CANCEL, 0);
     181          10 :         check_table(0, 0);
     182             : 
     183          10 :         printf("cancel twice, second does nothing\n");
     184          10 :         start_in();
     185          10 :         send_rows(1, 5);
     186          10 :         done(CS_BLK_CANCEL, 0);
     187          10 :         done(CS_BLK_CANCEL, 0);
     188          10 :         check_table(0, 0);
     189             : 
     190          10 :         printf("normal copy after cancellation\n");
     191          10 :         start_in();
     192          10 :         send_rows(1, 2000);
     193          10 :         done(CS_BLK_ALL, 2000);
     194          10 :         check_table(2000, sum_ids(1, 2000));
     195             : 
     196          10 :         printf("cancel copy out\n");
     197          10 :         check_call(blk_init, (blkdesc, CS_BLK_OUT, (char *) table_name, CS_NULLTERM));
     198          10 :         memset(&fmt, 0, sizeof(fmt));
     199          10 :         fmt.count = 1;
     200          10 :         fmt.datatype = CS_INT_TYPE;
     201          10 :         fmt.maxlength = sizeof(id);
     202          10 :         check_call(blk_bind, (blkdesc, 1, &fmt, &id, &id_len, NULL));
     203          60 :         for (i = 0; i < 5; i++) {
     204          50 :                 check_call(blk_rowxfer_mult, (blkdesc, &rows));
     205          50 :                 assert(rows == 1);
     206             :         }
     207          10 :         done(CS_BLK_CANCEL, 0);
     208          10 :         check_table(2000, sum_ids(1, 2000));
     209             : 
     210          10 :         blk_drop(blkdesc);
     211             : 
     212          10 :         sprintf(sql, "drop table %s", table_name);
     213          10 :         check_call(run_command, (cmd, sql));
     214             : 
     215          10 :         printf("done\n");
     216             : 
     217          10 :         check_call(try_ctlogout, (ctx, conn, cmd, verbose));
     218             : 
     219          10 :         return 0;
     220             : }

Generated by: LCOV version 1.13