LCOV - code coverage report
Current view: top level - src/odbc/unittests - attributes.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 80 125 64.0 %
Date: 2026-02-12 22:26:45 Functions: 5 6 83.3 %

          Line data    Source code
       1             : #include "common.h"
       2             : #include <ctype.h>
       3             : 
       4             : /*
       5             :  * SQLSetStmtAttr
       6             :  */
       7             : 
       8             : static int g_result = 0;
       9             : static unsigned int line_num;
      10             : 
      11             : static void TDS_NORETURN
      12           0 : fatal(const char *msg, ...)
      13             : {
      14             :         va_list ap;
      15             : 
      16           0 :         va_start(ap, msg);
      17           0 :         vfprintf(stderr, msg, ap);
      18           0 :         va_end(ap);
      19             : 
      20           0 :         exit(1);
      21             : }
      22             : 
      23             : static int
      24          64 : get_int(const char *s)
      25             : {
      26             :         char *end;
      27             :         long l;
      28             : 
      29          64 :         if (!s)
      30           0 :                 fatal("Line %u: NULL int\n", line_num);
      31          64 :         l = strtol(s, &end, 0);
      32          64 :         if (end[0])
      33           0 :                 fatal("Line %u: Invalid int\n", line_num);
      34          64 :         return (int) l;
      35             : }
      36             : 
      37             : static int
      38         128 : lookup(const char *name, const struct odbc_lookup_int *table)
      39             : {
      40         128 :         if (!table)
      41          56 :                 return get_int(name);
      42             : 
      43          56 :         for (; table->name; ++table)
      44         128 :                 if (strcmp(table->name, name) == 0)
      45          72 :                         return table->value;
      46             : 
      47           0 :         return get_int(name);
      48             : }
      49             : 
      50             : static const char *
      51             : unlookup(int value, const struct odbc_lookup_int *table)
      52             : {
      53           0 :         if (!table)
      54             :                 return "??";
      55             : 
      56           0 :         return odbc_lookup_value(value, table, "??");
      57             : }
      58             : 
      59             : static struct odbc_lookup_int concurrency[] = {
      60             : #define TYPE(s) { #s, s }
      61             :         TYPE(SQL_CONCUR_READ_ONLY),
      62             :         TYPE(SQL_CONCUR_LOCK),
      63             :         TYPE(SQL_CONCUR_ROWVER),
      64             :         TYPE(SQL_CONCUR_VALUES),
      65             : #undef TYPE
      66             :         { NULL, 0 }
      67             : };
      68             : 
      69             : static struct odbc_lookup_int scrollable[] = {
      70             : #define TYPE(s) { #s, s }
      71             :         TYPE(SQL_NONSCROLLABLE),
      72             :         TYPE(SQL_SCROLLABLE),
      73             : #undef TYPE
      74             :         { NULL, 0 }
      75             : };
      76             : 
      77             : static struct odbc_lookup_int sensitivity[] = {
      78             : #define TYPE(s) { #s, s }
      79             :         TYPE(SQL_UNSPECIFIED),
      80             :         TYPE(SQL_INSENSITIVE),
      81             :         TYPE(SQL_SENSITIVE),
      82             : #undef TYPE
      83             :         { NULL, 0 }
      84             : };
      85             : 
      86             : static struct odbc_lookup_int cursor_type[] = {
      87             : #define TYPE(s) { #s, s }
      88             :         TYPE(SQL_CURSOR_FORWARD_ONLY),
      89             :         TYPE(SQL_CURSOR_STATIC),
      90             :         TYPE(SQL_CURSOR_KEYSET_DRIVEN),
      91             :         TYPE(SQL_CURSOR_DYNAMIC),
      92             : #undef TYPE
      93             :         { NULL, 0 }
      94             : };
      95             : 
      96             : static struct odbc_lookup_int noscan[] = {
      97             : #define TYPE(s) { #s, s }
      98             :         TYPE(SQL_NOSCAN_OFF),
      99             :         TYPE(SQL_NOSCAN_ON),
     100             : #undef TYPE
     101             :         { NULL, 0 }
     102             : };
     103             : 
     104             : static struct odbc_lookup_int retrieve_data[] = {
     105             : #define TYPE(s) { #s, s }
     106             :         TYPE(SQL_RD_ON),
     107             :         TYPE(SQL_RD_OFF),
     108             : #undef TYPE
     109             :         { NULL, 0 }
     110             : };
     111             : 
     112             : static struct odbc_lookup_int simulate_cursor[] = {
     113             : #define TYPE(s) { #s, s }
     114             :         TYPE(SQL_SC_NON_UNIQUE),
     115             :         TYPE(SQL_SC_TRY_UNIQUE),
     116             :         TYPE(SQL_SC_UNIQUE),
     117             : #undef TYPE
     118             :         { NULL, 0 }
     119             : };
     120             : 
     121             : static struct odbc_lookup_int use_bookmarks[] = {
     122             : #define TYPE(s) { #s, s }
     123             :         TYPE(SQL_UB_OFF),
     124             :         TYPE(SQL_UB_VARIABLE),
     125             :         TYPE(SQL_UB_FIXED),
     126             : #undef TYPE
     127             :         { NULL, 0 }
     128             : };
     129             : 
     130             : typedef enum
     131             : {
     132             :         type_INTEGER,
     133             :         type_UINTEGER,
     134             :         type_SMALLINT,
     135             :         type_LEN,
     136             :         type_CHARP,
     137             :         type_DESC,
     138             :         type_VOIDP
     139             : } test_type_t;
     140             : 
     141             : struct attribute
     142             : {
     143             :         const char *name;
     144             :         int value;
     145             :         test_type_t type;
     146             :         const struct odbc_lookup_int *lookup;
     147             : };
     148             : 
     149             : static const struct attribute attributes[] = {
     150             : #define ATTR(s,t) { #s, s, type_##t, NULL }
     151             : #define ATTR2(s,t,l) { #s, s, type_##t, l }
     152             :         ATTR(SQL_ATTR_APP_PARAM_DESC, DESC),
     153             :         ATTR(SQL_ATTR_APP_ROW_DESC, DESC),
     154             :         ATTR(SQL_ATTR_ASYNC_ENABLE, UINTEGER),
     155             :         ATTR2(SQL_ATTR_CONCURRENCY, UINTEGER, concurrency),
     156             :         ATTR2(SQL_ATTR_CURSOR_SCROLLABLE, UINTEGER, scrollable),
     157             :         ATTR2(SQL_ATTR_CURSOR_SENSITIVITY, UINTEGER, sensitivity),
     158             :         ATTR2(SQL_ATTR_CURSOR_TYPE, UINTEGER, cursor_type),
     159             :         ATTR(SQL_ATTR_ENABLE_AUTO_IPD, UINTEGER),
     160             :         ATTR(SQL_ATTR_FETCH_BOOKMARK_PTR, VOIDP),
     161             :         ATTR(SQL_ATTR_IMP_PARAM_DESC, DESC),
     162             :         ATTR(SQL_ATTR_IMP_ROW_DESC, DESC),
     163             :         ATTR(SQL_ATTR_KEYSET_SIZE, UINTEGER),
     164             :         ATTR(SQL_ATTR_MAX_LENGTH, UINTEGER),
     165             :         ATTR(SQL_ATTR_MAX_ROWS, UINTEGER),
     166             :         ATTR(SQL_ATTR_METADATA_ID, UINTEGER),
     167             :         ATTR2(SQL_ATTR_NOSCAN, UINTEGER, noscan),
     168             :         ATTR(SQL_ATTR_PARAM_BIND_OFFSET_PTR, VOIDP),
     169             :         ATTR(SQL_ATTR_PARAM_BIND_OFFSET_PTR, VOIDP),
     170             :         ATTR(SQL_ATTR_PARAM_BIND_TYPE, UINTEGER),
     171             :         ATTR(SQL_ATTR_PARAM_OPERATION_PTR, VOIDP),
     172             :         ATTR(SQL_ATTR_PARAM_STATUS_PTR, VOIDP),
     173             :         ATTR(SQL_ATTR_PARAMS_PROCESSED_PTR, VOIDP),
     174             :         ATTR(SQL_ATTR_PARAMSET_SIZE, UINTEGER),
     175             :         ATTR(SQL_ATTR_QUERY_TIMEOUT, UINTEGER),
     176             :         ATTR2(SQL_ATTR_RETRIEVE_DATA, UINTEGER, retrieve_data),
     177             :         ATTR(SQL_ATTR_ROW_ARRAY_SIZE, UINTEGER),
     178             :         ATTR(SQL_ATTR_ROW_BIND_OFFSET_PTR, VOIDP),
     179             :         ATTR(SQL_ATTR_ROW_BIND_TYPE, UINTEGER),
     180             :         ATTR(SQL_ATTR_ROW_NUMBER, UINTEGER),
     181             :         ATTR(SQL_ATTR_ROW_OPERATION_PTR, VOIDP),
     182             :         ATTR(SQL_ATTR_ROW_STATUS_PTR, VOIDP),
     183             :         ATTR(SQL_ATTR_ROWS_FETCHED_PTR, VOIDP),
     184             :         ATTR2(SQL_ATTR_SIMULATE_CURSOR, UINTEGER, simulate_cursor),
     185             :         ATTR2(SQL_ATTR_USE_BOOKMARKS, UINTEGER, use_bookmarks),
     186             : #undef ATTR2
     187             : #undef ATTR
     188             : };
     189             : 
     190             : static const struct attribute *
     191         128 : lookup_attr(const char *name)
     192             : {
     193             :         unsigned int i;
     194             : 
     195         128 :         if (!name)
     196           0 :                 fatal("Line %u: NULL attribute\n", line_num);
     197        1616 :         for (i = 0; i < TDS_VECTOR_SIZE(attributes); ++i)
     198        1744 :                 if (strcmp(attributes[i].name, name) == 0 || strcmp(attributes[i].name + 4, name) == 0)
     199         128 :                         return &attributes[i];
     200           0 :         fatal("Line %u: attribute %s not found\n", line_num, name);
     201             :         return NULL;
     202             : }
     203             : 
     204             : #define SEP " \t\n"
     205             : 
     206             : #define ATTR_PARAMS \
     207             :         const struct attribute *attr TDS_UNUSED, \
     208             :         int expected TDS_UNUSED
     209             : typedef int (*get_attr_t) (ATTR_PARAMS);
     210             : 
     211             : static int
     212          96 : get_attr_stmt(ATTR_PARAMS)
     213             : {
     214             :         SQLINTEGER i, ind;
     215             :         SQLSMALLINT si;
     216             :         SQLLEN li;
     217             :         SQLRETURN ret;
     218             : 
     219          96 :         ret = SQL_ERROR;
     220          96 :         switch (attr->type) {
     221          96 :         case type_INTEGER:
     222             :         case type_UINTEGER:
     223          96 :                 i = 0xdeadbeef;
     224          96 :                 ret = SQLGetStmtAttr(odbc_stmt, attr->value, (SQLPOINTER) & i, sizeof(SQLINTEGER), &ind);
     225          96 :                 break;
     226           0 :         case type_SMALLINT:
     227           0 :                 si = 0xbeef;
     228           0 :                 ret = SQLGetStmtAttr(odbc_stmt, attr->value, (SQLPOINTER) & si, sizeof(SQLSMALLINT), &ind);
     229           0 :                 i = si;
     230           0 :                 break;
     231           0 :         case type_LEN:
     232           0 :                 li = 0xdeadbeef;
     233           0 :                 ret = SQLGetStmtAttr(odbc_stmt, attr->value, (SQLPOINTER) & li, sizeof(SQLLEN), &ind);
     234           0 :                 i = li;
     235           0 :                 break;
     236           0 :         case type_VOIDP:
     237             :         case type_DESC:
     238             :         case type_CHARP:
     239           0 :                 fatal("Line %u: CHAR* check still not supported\n", line_num);
     240             :                 break;
     241             :         }
     242          96 :         if (!SQL_SUCCEEDED(ret))
     243           0 :                 fatal("Line %u: failure not expected\n", line_num);
     244          96 :         return i;
     245             : }
     246             : 
     247             : #if 0
     248             : /* do not retry any attribute just return expected value so to make caller happy */
     249             : static int
     250             : get_attr_none(ATTR_PARAMS)
     251             : {
     252             :         return expected;
     253             : }
     254             : #endif
     255             : 
     256          10 : TEST_MAIN()
     257             : {
     258             : #define TEST_FILE "attributes.in"
     259          10 :         const char *in_file = FREETDS_SRCDIR "/" TEST_FILE;
     260             :         FILE *f;
     261             :         char buf[256];
     262             :         SQLINTEGER i;
     263             :         SQLLEN len;
     264          10 :         get_attr_t get_attr_p = get_attr_stmt;
     265             : 
     266          10 :         odbc_connect();
     267             :         /* TODO find another way */
     268          10 :         odbc_check_cursor();
     269           8 :         odbc_command("SET TEXTSIZE 4096");
     270             : 
     271           8 :         SQLBindCol(odbc_stmt, 1, SQL_C_SLONG, &i, sizeof(i), &len);
     272             : 
     273           8 :         f = fopen(in_file, "r");
     274           8 :         if (!f)
     275           0 :                 f = fopen(TEST_FILE, "r");
     276           8 :         if (!f) {
     277           0 :                 fprintf(stderr, "error opening test file\n");
     278           0 :                 exit(1);
     279             :         }
     280             : 
     281           8 :         line_num = 0;
     282         272 :         while (fgets(buf, sizeof(buf), f)) {
     283         256 :                 char *p = buf, *cmd;
     284             : 
     285         256 :                 ++line_num;
     286             : 
     287         568 :                 while (isspace((unsigned char) *p))
     288          56 :                         ++p;
     289         256 :                 cmd = strtok(p, SEP);
     290             : 
     291             :                 /* skip comments */
     292         256 :                 if (!cmd || cmd[0] == '#' || cmd[0] == 0 || cmd[0] == '\n')
     293         112 :                         continue;
     294             : 
     295         144 :                 if (strcmp(cmd, "odbc") == 0) {
     296           8 :                         bool odbc3 = get_int(strtok(NULL, SEP)) == 3;
     297             : 
     298           8 :                         if (odbc_use_version3 != odbc3) {
     299           8 :                                 odbc_use_version3 = odbc3;
     300           8 :                                 odbc_disconnect();
     301           8 :                                 odbc_connect();
     302           8 :                                 odbc_command("SET TEXTSIZE 4096");
     303           8 :                                 SQLBindCol(odbc_stmt, 1, SQL_C_SLONG, &i, sizeof(i), &len);
     304             :                         }
     305           8 :                         continue;
     306             :                 }
     307             : 
     308             :                 /* set attribute */
     309         136 :                 if (strcmp(cmd, "set") == 0) {
     310          32 :                         const struct attribute *attr = lookup_attr(strtok(NULL, SEP));
     311          32 :                         char *value = strtok(NULL, SEP);
     312             :                         SQLRETURN ret;
     313             : 
     314          32 :                         if (!value)
     315           0 :                                 fatal("Line %u: value not defined\n", line_num);
     316             : 
     317          32 :                         ret = SQL_ERROR;
     318          32 :                         switch (attr->type) {
     319          32 :                         case type_UINTEGER:
     320             :                         case type_INTEGER:
     321          32 :                                 ret = SQLSetStmtAttr(odbc_stmt, attr->value, TDS_INT2PTR(lookup(value, attr->lookup)),
     322             :                                                       sizeof(SQLINTEGER));
     323          32 :                                 break;
     324           0 :                         case type_SMALLINT:
     325           0 :                                 ret = SQLSetStmtAttr(odbc_stmt, attr->value, TDS_INT2PTR(lookup(value, attr->lookup)),
     326             :                                                       sizeof(SQLSMALLINT));
     327           0 :                                 break;
     328           0 :                         case type_LEN:
     329           0 :                                 ret = SQLSetStmtAttr(odbc_stmt, attr->value, TDS_INT2PTR(lookup(value, attr->lookup)),
     330             :                                                       sizeof(SQLLEN));
     331           0 :                                 break;
     332           0 :                         case type_CHARP:
     333           0 :                                 ret = SQLSetStmtAttr(odbc_stmt, attr->value, (SQLPOINTER) value, SQL_NTS);
     334           0 :                                 break;
     335           0 :                         case type_VOIDP:
     336             :                         case type_DESC:
     337           0 :                                 fatal("Line %u: not implemented\n");
     338             :                         }
     339          32 :                         if (!SQL_SUCCEEDED(ret))
     340           0 :                                 fatal("Line %u: failure not expected setting statement attribute\n", line_num);
     341          32 :                         get_attr_p = get_attr_stmt;
     342          32 :                         continue;
     343             :                 }
     344             : 
     345             :                 /* test attribute */
     346         104 :                 if (strcmp(cmd, "attr") == 0) {
     347          96 :                         const struct attribute *attr = lookup_attr(strtok(NULL, SEP));
     348          96 :                         char *value = strtok(NULL, SEP);
     349          96 :                         int i, expected = lookup(value, attr->lookup);
     350             : 
     351          96 :                         if (!value)
     352           0 :                                 fatal("Line %u: value not defined\n", line_num);
     353             : 
     354          96 :                         i = get_attr_p(attr, expected);
     355          96 :                         if (i != expected) {
     356           0 :                                 g_result = 1;
     357           0 :                                 fprintf(stderr, "Line %u: invalid %s got %d(%s) expected %s\n", line_num, attr->name, i, unlookup(i, attr->lookup), value);
     358             :                         }
     359          96 :                         continue;
     360             :                 }
     361             : 
     362           8 :                 if (strcmp(cmd, "reset") == 0) {
     363           8 :                         odbc_reset_statement();
     364           8 :                         continue;
     365             :                 }
     366             : 
     367           0 :                 fatal("Line %u: command '%s' not handled\n", line_num, cmd);
     368             :         }
     369             : 
     370           8 :         fclose(f);
     371           8 :         odbc_disconnect();
     372             : 
     373           8 :         printf("Done.\n");
     374           8 :         return g_result;
     375             : }

Generated by: LCOV version 1.13