LCOV - code coverage report
Current view: top level - src/odbc/unittests - bcp.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 171 182 94.0 %
Date: 2025-02-21 09:36:06 Functions: 9 9 100.0 %

          Line data    Source code
       1             : #include "common.h"
       2             : #define TDSODBC_BCP
       3             : #include <odbcss.h>
       4             : #include <assert.h>
       5             : 
       6             : #ifdef UNICODE
       7             : typedef SQLWCHAR bcp_init_char_t;
       8             : #else
       9             : typedef char bcp_init_char_t;
      10             : #endif
      11             : 
      12             : struct prefixed_int {
      13             :         ODBCINT64 prefix;
      14             :         int value;
      15             : };
      16             : struct prefixed_str {
      17             :         ODBCINT64 prefix;
      18             :         char value[64];
      19             : };
      20             : 
      21             : /*
      22             :  * Static data for insertion
      23             :  */
      24             : static struct prefixed_int not_null_bit           = {4, 1};
      25             : static struct prefixed_str not_null_char          = {64, "a char"};
      26             : static struct prefixed_str not_null_varchar       = {64, "a varchar"};
      27             : static struct prefixed_str not_null_datetime      = {64, "2003-12-17 15:44:00.000"};
      28             : static struct prefixed_str not_null_smalldatetime = {64, "2003-12-17 15:44:00"};
      29             : static struct prefixed_str not_null_money         = {64, "12.341"};
      30             : static struct prefixed_str not_null_smallmoney    = {64, "12.34"};
      31             : static struct prefixed_str not_null_float         = {64, "12.34"};
      32             : static struct prefixed_str not_null_real          = {64, "12.34"};
      33             : static struct prefixed_str not_null_decimal       = {64, "12.34"};
      34             : static struct prefixed_str not_null_numeric       = {64, "12.34"};
      35             : static struct prefixed_int not_null_int           = {4, 1234};
      36             : static struct prefixed_int not_null_smallint      = {4, 1234};
      37             : static struct prefixed_int not_null_tinyint       = {4, 123};
      38             : static struct prefixed_str not_null_nvarchar      = {64, "a wide var"};
      39             : static ODBCINT64 null_prefix = -1;
      40             : 
      41             : static const char *expected[] = {
      42             :         "1",
      43             :         "a char    ","a varchar","2003-12-17 15:44:00.000","2003-12-17 15:44:00",
      44             :         "12.3410","12.3400","12.34","12.3400002","12.34","12.34",
      45             :         "1234","1234","123",
      46             :         "a wide var",
      47             : };
      48             : static const int total_cols = 29;
      49             : 
      50             : static const char *expected_special[] = {
      51             :         "2015-03-14 15:26:53.000",
      52             :         "2015-03-14 15:26:53.589793",
      53             :         "3.141593000",
      54             :         "3.141593",           /* MS driver has "3141593" here. Bug? Should we be bug-compatible? */
      55             :         "",
      56             : };
      57             : 
      58             : static int tds_version;
      59             : 
      60             : static void
      61          20 : cleanup(void)
      62             : {
      63          20 :         odbc_command("if exists (select 1 from sysobjects where type = 'U' and name = 'all_types_bcp_unittest') drop table all_types_bcp_unittest");
      64          20 :         odbc_command("if exists (select 1 from sysobjects where type = 'U' and name = 'special_types_bcp_unittest') drop table special_types_bcp_unittest");
      65          20 : }
      66             : 
      67             : static void
      68          10 : init(void)
      69             : {
      70          10 :         cleanup();
      71             : 
      72          10 :         odbc_command("CREATE TABLE all_types_bcp_unittest ("
      73             :                 "  not_null_bit                  bit NOT NULL"
      74             :                 ""
      75             :                 ", not_null_char                 char(10) NOT NULL"
      76             :                 ", not_null_varchar              varchar(10) NOT NULL"
      77             :                 ""
      78             :                 ", not_null_datetime             datetime NOT NULL"
      79             :                 ", not_null_smalldatetime        smalldatetime NOT NULL"
      80             :                 ""
      81             :                 ", not_null_money                money NOT NULL"
      82             :                 ", not_null_smallmoney           smallmoney NOT NULL"
      83             :                 ""
      84             :                 ", not_null_float                float NOT NULL"
      85             :                 ", not_null_real                 real NOT NULL"
      86             :                 ""
      87             :                 ", not_null_decimal              decimal(5,2) NOT NULL"
      88             :                 ", not_null_numeric              numeric(5,2) NOT NULL"
      89             :                 ""
      90             :                 ", not_null_int                  int NOT NULL"
      91             :                 ", not_null_smallint             smallint NOT NULL"
      92             :                 ", not_null_tinyint              tinyint NOT NULL"
      93             :                 ", not_null_nvarchar             nvarchar(10) NOT NULL"
      94             :                 ""
      95             :                 ", nullable_char                 char(10)  NULL"
      96             :                 ", nullable_varchar              varchar(10)  NULL"
      97             :                 ""
      98             :                 ", nullable_datetime             datetime  NULL"
      99             :                 ", nullable_smalldatetime        smalldatetime  NULL"
     100             :                 ""
     101             :                 ", nullable_money                money  NULL"
     102             :                 ", nullable_smallmoney           smallmoney  NULL"
     103             :                 ""
     104             :                 ", nullable_float                float  NULL"
     105             :                 ", nullable_real                 real  NULL"
     106             :                 ""
     107             :                 ", nullable_decimal              decimal(5,2)  NULL"
     108             :                 ", nullable_numeric              numeric(5,2)  NULL"
     109             :                 ""
     110             :                 ", nullable_int                  int  NULL"
     111             :                 ", nullable_smallint             smallint  NULL"
     112             :                 ", nullable_tinyint              tinyint  NULL"
     113             :                 ", nullable_nvarchar             nvarchar(10)  NULL"
     114             :                 ")");
     115             : 
     116          10 :         if (tds_version < 0x703)
     117             :                 return;
     118             : 
     119             :                 /* Excludes:
     120             :                  * binary
     121             :                  * image
     122             :                  * uniqueidentifier
     123             :                  * varbinary
     124             :                  * text
     125             :                  * timestamp
     126             :                  * nchar
     127             :                  * ntext
     128             :                  * nvarchar
     129             :                  */
     130           4 :         odbc_command("CREATE TABLE special_types_bcp_unittest ("
     131             :                 "dt datetime not null,"
     132             :                 "dt2 datetime2(6) not null,"
     133             :                 "num decimal(19,9) not null,"
     134             :                 "numstr varchar(64) not null,"
     135             :                 "empty varchar(64) not null,"
     136             :                 "bitnull bit null"
     137             :                 ")");
     138             : }
     139             : 
     140             : #define VARCHAR_BIND(x) \
     141             :         bcp_bind( odbc_conn, (prefixlen == 0 ? (void*)&x.value : (void*)&x.prefix), prefixlen, \
     142             :                 strlen(x.value), NULL, termlen, BCP_TYPE_SQLVARCHAR, col++ )
     143             : 
     144             : #define INT_BIND(x) \
     145             :         bcp_bind( odbc_conn, (prefixlen == 0 ? (void*)&x.value : (void*)&x.prefix), prefixlen, \
     146             :                 SQL_VARLEN_DATA, NULL, termlen, BCP_TYPE_SQLINT4,    col++ )
     147             : 
     148             : #define NULL_BIND(x, type) \
     149             :         bcp_bind( odbc_conn, (prefixlen == 0 ? (void*)&x.value : (void*)&null_prefix), prefixlen, \
     150             :                 prefixlen == 0 ? SQL_NULL_DATA : SQL_VARLEN_DATA, NULL, termlen, type,    col++ )
     151             : 
     152             : static void
     153          20 : test_bind(int prefixlen)
     154             : {
     155             :         enum { termlen = 0 };
     156             : 
     157             :         RETCODE fOK;
     158          20 :         int col=1;
     159             : 
     160             :         /* non nulls */
     161          40 :         fOK = INT_BIND(not_null_bit);
     162          20 :         assert(fOK == SUCCEED);
     163             : 
     164          40 :         fOK = VARCHAR_BIND(not_null_char);
     165          20 :         assert(fOK == SUCCEED);
     166          40 :         fOK = VARCHAR_BIND(not_null_varchar);
     167          20 :         assert(fOK == SUCCEED);
     168             : 
     169          40 :         fOK = VARCHAR_BIND(not_null_datetime);
     170          20 :         assert(fOK == SUCCEED);
     171          40 :         fOK = VARCHAR_BIND(not_null_smalldatetime);
     172          20 :         assert(fOK == SUCCEED);
     173             : 
     174          40 :         fOK = VARCHAR_BIND(not_null_money);
     175          20 :         assert(fOK == SUCCEED);
     176          40 :         fOK = VARCHAR_BIND(not_null_smallmoney);
     177          20 :         assert(fOK == SUCCEED);
     178             : 
     179          40 :         fOK = VARCHAR_BIND(not_null_float);
     180          20 :         assert(fOK == SUCCEED);
     181          40 :         fOK = VARCHAR_BIND(not_null_real);
     182          20 :         assert(fOK == SUCCEED);
     183             : 
     184          40 :         fOK = VARCHAR_BIND(not_null_decimal);
     185          20 :         assert(fOK == SUCCEED);
     186          40 :         fOK = VARCHAR_BIND(not_null_numeric);
     187          20 :         assert(fOK == SUCCEED);
     188             : 
     189          40 :         fOK = INT_BIND(not_null_int);
     190          20 :         assert(fOK == SUCCEED);
     191          40 :         fOK = INT_BIND(not_null_smallint);
     192          20 :         assert(fOK == SUCCEED);
     193          40 :         fOK = INT_BIND(not_null_tinyint);
     194          20 :         assert(fOK == SUCCEED);
     195          40 :         fOK = VARCHAR_BIND(not_null_nvarchar);
     196          20 :         assert(fOK == SUCCEED);
     197             : 
     198             :         /* nulls */
     199             :         assert(fOK == SUCCEED);
     200          40 :         fOK = NULL_BIND(not_null_char, BCP_TYPE_SQLVARCHAR);
     201          20 :         assert(fOK == SUCCEED);
     202          40 :         fOK = NULL_BIND(not_null_varchar, BCP_TYPE_SQLVARCHAR);
     203          20 :         assert(fOK == SUCCEED);
     204             : 
     205          40 :         fOK = NULL_BIND(not_null_datetime, BCP_TYPE_SQLVARCHAR);
     206          20 :         assert(fOK == SUCCEED);
     207          40 :         fOK = NULL_BIND(not_null_smalldatetime, BCP_TYPE_SQLVARCHAR);
     208          20 :         assert(fOK == SUCCEED);
     209             : 
     210          40 :         fOK = NULL_BIND(not_null_money, BCP_TYPE_SQLVARCHAR);
     211          20 :         assert(fOK == SUCCEED);
     212          40 :         fOK = NULL_BIND(not_null_smallmoney, BCP_TYPE_SQLVARCHAR);
     213          20 :         assert(fOK == SUCCEED);
     214             : 
     215          40 :         fOK = NULL_BIND(not_null_float, BCP_TYPE_SQLVARCHAR);
     216          20 :         assert(fOK == SUCCEED);
     217          40 :         fOK = NULL_BIND(not_null_real, BCP_TYPE_SQLVARCHAR);
     218          20 :         assert(fOK == SUCCEED);
     219             : 
     220          40 :         fOK = NULL_BIND(not_null_decimal, BCP_TYPE_SQLVARCHAR);
     221          20 :         assert(fOK == SUCCEED);
     222          40 :         fOK = NULL_BIND(not_null_numeric, BCP_TYPE_SQLVARCHAR);
     223          20 :         assert(fOK == SUCCEED);
     224             : 
     225          40 :         fOK = NULL_BIND(not_null_int, BCP_TYPE_SQLINT4);
     226          20 :         assert(fOK == SUCCEED);
     227          40 :         fOK = NULL_BIND(not_null_smallint, BCP_TYPE_SQLINT4);
     228          20 :         assert(fOK == SUCCEED);
     229          40 :         fOK = NULL_BIND(not_null_tinyint, BCP_TYPE_SQLINT4);
     230          20 :         assert(fOK == SUCCEED);
     231          40 :         fOK = NULL_BIND(not_null_nvarchar, BCP_TYPE_SQLVARCHAR);
     232          20 :         assert(fOK == SUCCEED);
     233             : 
     234          20 : }
     235             : 
     236             : static void
     237          10 : set_attr(void)
     238             : {
     239          10 :         SQLSetConnectAttr(odbc_conn, SQL_COPT_SS_BCP, (SQLPOINTER)SQL_BCP_ON, 0);
     240          10 : }
     241             : 
     242             : static void
     243             : report_bcp_error(const char *errmsg, int line, const char *file)
     244             : {
     245           0 :         odbc_stmt = NULL;
     246           0 :         odbc_report_error(errmsg, line, file);
     247             : }
     248             : 
     249             : static void normal_inserts(int prefixlen);
     250             : static void normal_select(void);
     251             : static void special_inserts(void);
     252             : static void special_select(void);
     253             : 
     254             : static const char table_name[] = "all_types_bcp_unittest";
     255             : 
     256             : int
     257          10 : main(void)
     258             : {
     259             :         const char *s;
     260             : 
     261          10 :         odbc_set_conn_attr = set_attr;
     262          10 :         odbc_connect();
     263             : 
     264          10 :         tds_version = odbc_tds_version();
     265             : 
     266          10 :         init();
     267             : 
     268          10 :         normal_inserts(0);
     269          10 :         if (tds_version >= 0x703)
     270           4 :                 special_inserts();
     271          10 :         normal_select();
     272          10 :         if (tds_version >= 0x703)
     273           4 :                 special_select();
     274             : 
     275          10 :         odbc_command("delete from all_types_bcp_unittest");
     276          10 :         normal_inserts(8);
     277          10 :         normal_select();
     278             : 
     279          10 :         if ((s = getenv("BCP")) != NULL && 0 == strcmp(s, "nodrop")) {
     280           0 :                 printf("BCP=nodrop: '%s' kept\n", table_name);
     281             :         } else {
     282          10 :                 printf("Dropping table %s\n", table_name);
     283          10 :                 odbc_command("drop table all_types_bcp_unittest");
     284          10 :                 if (tds_version >= 0x703)
     285           4 :                         odbc_command("drop table special_types_bcp_unittest");
     286             :         }
     287             : 
     288          10 :         cleanup();
     289             : 
     290          10 :         odbc_disconnect();
     291             : 
     292          10 :         printf("Done.\n");
     293             :         return 0;
     294             : }
     295             : 
     296             : static void
     297          20 : normal_inserts(int prefixlen)
     298             : {
     299             :         int i;
     300             :         int rows_sent;
     301             : 
     302             :         /* set up and send the bcp */
     303          20 :         printf("preparing to insert into %s ... ", table_name);
     304          40 :         if (bcp_init(odbc_conn, (bcp_init_char_t *) T(table_name), NULL, NULL, BCP_DIRECTION_IN) == FAIL)
     305             :                 report_bcp_error("bcp_init", __LINE__, __FILE__);
     306          20 :         printf("OK\n");
     307             : 
     308          20 :         test_bind(prefixlen);
     309             : 
     310          20 :         printf("Sending same row 10 times... \n");
     311         220 :         for (i=0; i<10; i++)
     312         400 :                 if (bcp_sendrow(odbc_conn) == FAIL)
     313             :                         report_bcp_error("bcp_sendrow", __LINE__, __FILE__);
     314             : 
     315             : #if 1
     316          20 :         rows_sent = bcp_batch(odbc_conn);
     317          20 :         if (rows_sent == -1)
     318             :                 report_bcp_error("bcp_batch", __LINE__, __FILE__);
     319             : #endif
     320             : 
     321          20 :         printf("OK\n");
     322             : 
     323             :         /* end bcp.  */
     324          20 :         rows_sent = bcp_done(odbc_conn);
     325          20 :         if (rows_sent != 0)
     326             :                 report_bcp_error("bcp_done", __LINE__, __FILE__);
     327             :         else
     328          20 :                 printf("%d rows copied.\n", rows_sent);
     329             : 
     330          20 :         printf("done\n");
     331          20 : }
     332             : 
     333             : static void
     334           4 : special_inserts(void)
     335             : {
     336             :         int rows_sent;
     337             :         SQL_TIMESTAMP_STRUCT timestamp;
     338             :         DBDATETIME datetime;
     339             :         SQL_NUMERIC_STRUCT numeric;
     340             : 
     341           4 :         printf("sending special types\n");
     342           4 :         rows_sent = 0;
     343             : 
     344           8 :         if (bcp_init(odbc_conn, (bcp_init_char_t *) T("special_types_bcp_unittest"), NULL, NULL, BCP_DIRECTION_IN) == FAIL)
     345             :                 report_bcp_error("bcp_init", __LINE__, __FILE__);
     346           4 :         printf("OK\n");
     347             : 
     348           8 :         if (bcp_control(odbc_conn, BCPHINTSA, (void *) "TABLOCK") != SUCCEED)
     349             :                 report_bcp_error("bcp_init", __LINE__, __FILE__);
     350             : 
     351           4 :         datetime.dtdays = 42075;
     352           4 :         datetime.dttime = 16683900;
     353           4 :         timestamp.year = 2015;
     354           4 :         timestamp.month = 3;
     355           4 :         timestamp.day = 14;
     356           4 :         timestamp.hour = 15;
     357           4 :         timestamp.minute = 26;
     358           4 :         timestamp.second = 53;
     359           4 :         timestamp.fraction = 589793238;
     360           4 :         memset(&numeric, 0, sizeof(numeric));
     361           4 :         numeric.precision = 19;
     362           4 :         numeric.scale = 6;
     363           4 :         numeric.sign = 1;
     364           4 :         numeric.val[0] = 0xd9;
     365           4 :         numeric.val[1] = 0xef;
     366           4 :         numeric.val[2] = 0x2f;
     367           8 :         bcp_bind(odbc_conn, (unsigned char *) &datetime, 0, sizeof(datetime), NULL, 0, BCP_TYPE_SQLDATETIME, 1);
     368           8 :         bcp_bind(odbc_conn, (unsigned char *) &timestamp, 0, sizeof(timestamp), NULL, 0, BCP_TYPE_SQLDATETIME2, 2);
     369           8 :         bcp_bind(odbc_conn, (unsigned char *) &numeric, 0, sizeof(numeric), NULL, 0, BCP_TYPE_SQLDECIMAL, 3);
     370           8 :         bcp_bind(odbc_conn, (unsigned char *) &numeric, 0, sizeof(numeric), NULL, 0, BCP_TYPE_SQLDECIMAL, 4);
     371           8 :         bcp_bind(odbc_conn, (unsigned char *) "", 0, 0, NULL, 0, BCP_TYPE_SQLVARCHAR, 5);
     372           8 :         bcp_bind(odbc_conn, (unsigned char *) &not_null_bit, 0, SQL_NULL_DATA, NULL, 0, BCP_TYPE_SQLINT4, 6);
     373             : 
     374           8 :         if (bcp_sendrow(odbc_conn) == FAIL)
     375             :                 report_bcp_error("bcp_sendrow", __LINE__, __FILE__);
     376             : 
     377           4 :         rows_sent = bcp_batch(odbc_conn);
     378           4 :         if (rows_sent != 1)
     379             :                 report_bcp_error("bcp_batch", __LINE__, __FILE__);
     380             : 
     381           4 :         printf("OK\n");
     382             : 
     383             :         /* end bcp.  */
     384             : 
     385           4 :         rows_sent = bcp_done(odbc_conn);
     386           4 :         if (rows_sent != 0)
     387             :                 report_bcp_error("bcp_done", __LINE__, __FILE__);
     388             :         else
     389           4 :                 printf("%d rows copied.\n", rows_sent);
     390             : 
     391           4 :         printf("done\n");
     392           4 : }
     393             : 
     394             : static void
     395          20 : normal_select(void)
     396             : {
     397          20 :         int ok = 1, i;
     398             : 
     399          20 :         odbc_command("select * from all_types_bcp_unittest");
     400          20 :         CHKFetch("SI");
     401             : 
     402             :         /* first columns have values */
     403         320 :         for (i = 0; i < TDS_VECTOR_SIZE(expected); ++i) {
     404             :                 char output[128];
     405             :                 SQLLEN dataSize;
     406         300 :                 CHKGetData(i + 1, SQL_C_CHAR, output, sizeof(output), &dataSize, "S");
     407         300 :                 if (strcmp(output, expected[i]) || dataSize <= 0) {
     408           0 :                         fprintf(stderr, "Invalid returned col %d: '%s'!='%s'\n", i, expected[i], output);
     409           0 :                         ok = 0;
     410             :                 }
     411             :         }
     412             :         /* others are NULL */
     413         280 :         for (; i < total_cols; ++i) {
     414             :                 char output[128];
     415             :                 SQLLEN dataSize;
     416         280 :                 CHKGetData(i + 1, SQL_C_CHAR, output, sizeof(output), &dataSize, "S");
     417         280 :                 if (dataSize != SQL_NULL_DATA) {
     418           0 :                         fprintf(stderr, "Invalid returned col %d: should be NULL'\n", i);
     419           0 :                         ok = 0;
     420             :                 }
     421             :         }
     422          20 :         if (!ok)
     423           0 :                 exit(1);
     424          20 :         CHKCloseCursor("SI");
     425          20 : }
     426             : 
     427             : static void
     428           4 : special_select(void)
     429             : {
     430           4 :         int ok = 1, i;
     431             : 
     432           4 :         odbc_command("select top 1 * from special_types_bcp_unittest");
     433           4 :         CHKFetch("SI");
     434          24 :         for (i = 0; i < TDS_VECTOR_SIZE(expected_special); ++i) {
     435             :                 char output[128];
     436             :                 SQLLEN dataSize;
     437          20 :                 CHKGetData(i + 1, SQL_C_CHAR, output, sizeof(output), &dataSize, "S");
     438          20 :                 if (strcmp(output, expected_special[i]) || (dataSize <= 0 && expected_special[i][0] != '\0')) {
     439           0 :                         fprintf(stderr, "Invalid returned col %d: '%s'!='%s'\n", i, expected_special[i], output);
     440           0 :                         ok = 0;
     441             :                 }
     442             :         }
     443           4 :         if (!ok)
     444           0 :                 exit(1);
     445           4 :         CHKCloseCursor("SI");
     446           4 : }

Generated by: LCOV version 1.13