LCOV - code coverage report
Current view: top level - src/odbc/unittests - describecol.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 118 182 64.8 %
Date: 2025-07-26 14:18:55 Functions: 7 9 77.8 %

          Line data    Source code
       1             : #include "common.h"
       2             : #include <ctype.h>
       3             : #include "parser.h"
       4             : #include <freetds/bool.h>
       5             : 
       6             : /*
       7             :  * SQLDescribeCol test for precision
       8             :  * test what say SQLDescribeCol about precision using some type
       9             :  */
      10             : 
      11             : static int g_result = 0;
      12             : 
      13             : static int
      14        4106 : get_int(const char *s, odbc_parser *parser)
      15             : {
      16             :         char *end;
      17             :         long l;
      18             : 
      19        4106 :         if (!s)
      20           0 :                 odbc_fatal(parser, ": NULL int\n");
      21        4106 :         l = strtol(s, &end, 0);
      22        4106 :         if (end[0])
      23           0 :                 odbc_fatal(parser, ": Invalid int\n");
      24        4106 :         return (int) l;
      25             : }
      26             : 
      27             : static int
      28        4454 : lookup(const char *name, const struct odbc_lookup_int *table, odbc_parser *parser)
      29             : {
      30        4454 :         if (!table)
      31        4096 :                 return get_int(name, parser);
      32             : 
      33        4600 :         for (; table->name; ++table)
      34        4958 :                 if (strcmp(table->name, name) == 0)
      35         358 :                         return table->value;
      36             : 
      37           0 :         return get_int(name, parser);
      38             : }
      39             : 
      40             : static const char*
      41           0 : unlookup(long int value, const struct odbc_lookup_int *table)
      42             : {
      43             :         static char buf[32];
      44             : 
      45           0 :         sprintf(buf, "%ld", value);
      46           0 :         if (!table)
      47             :                 return buf;
      48             : 
      49           0 :         return odbc_lookup_value(value, table, buf);
      50             : }
      51             : 
      52             : static struct odbc_lookup_int sql_bools[] = {
      53             :         { "SQL_TRUE",  SQL_TRUE  },
      54             :         { "SQL_FALSE", SQL_FALSE },
      55             :         { NULL, 0 }
      56             : };
      57             : 
      58             : typedef enum
      59             : {
      60             :         type_INTEGER,
      61             :         type_SMALLINT,
      62             :         type_LEN,
      63             :         type_CHARP
      64             : } test_type_t;
      65             : 
      66             : struct attribute
      67             : {
      68             :         const char *name;
      69             :         int value;
      70             :         test_type_t type;
      71             :         const struct odbc_lookup_int *lookup;
      72             : };
      73             : 
      74             : static const struct attribute attributes[] = {
      75             : #define ATTR(s,t) { #s, s, type_##t, NULL }
      76             : #define ATTR2(s,t,l) { #s, s, type_##t, l }
      77             :         ATTR(SQL_COLUMN_LENGTH, INTEGER),
      78             :         ATTR(SQL_COLUMN_PRECISION, INTEGER),
      79             :         ATTR(SQL_COLUMN_SCALE, INTEGER),
      80             :         ATTR(SQL_DESC_LENGTH, LEN),
      81             :         ATTR(SQL_DESC_OCTET_LENGTH, LEN),
      82             :         ATTR(SQL_DESC_PRECISION, SMALLINT),
      83             :         ATTR(SQL_DESC_SCALE, SMALLINT),
      84             :         ATTR(SQL_DESC_DISPLAY_SIZE, INTEGER),
      85             :         ATTR(SQL_DESC_TYPE_NAME, CHARP),
      86             :         ATTR2(SQL_DESC_CONCISE_TYPE, SMALLINT, odbc_sql_types),
      87             :         ATTR2(SQL_DESC_TYPE, SMALLINT, odbc_sql_types),
      88             :         ATTR2(SQL_DESC_UNSIGNED, SMALLINT, sql_bools)
      89             : #undef ATTR2
      90             : #undef ATTR
      91             : };
      92             : 
      93             : static const struct attribute *
      94        6130 : lookup_attr(const char *name, odbc_parser *parser)
      95             : {
      96             :         unsigned int i;
      97             : 
      98        6130 :         if (!name)
      99           0 :                 odbc_fatal(parser, ": NULL attribute\n");
     100       28290 :         for (i = 0; i < TDS_VECTOR_SIZE(attributes); ++i)
     101       34420 :                 if (strcmp(attributes[i].name, name) == 0 || strcmp(attributes[i].name + 4, name) == 0)
     102        6130 :                         return &attributes[i];
     103           0 :         odbc_fatal(parser, ": attribute %s not found\n", name);
     104             :         return NULL;
     105             : }
     106             : 
     107             : #define ATTR_PARAMS \
     108             :         const struct attribute *attr TDS_UNUSED, \
     109             :         const char *expected_values[] TDS_UNUSED, \
     110             :         odbc_parser *parser TDS_UNUSED
     111             : typedef void (*check_attr_t) (ATTR_PARAMS);
     112             : 
     113             : static bool
     114             : is_contained(const char *s, const char *list[])
     115             : {
     116           0 :         for (;*list; ++list) {
     117         144 :                 if (strcmp(s, *list) == 0)
     118             :                         return true;
     119             :         }
     120             :         return false;
     121             : }
     122             : 
     123             : static bool
     124             : is_contained_lookup(SQLLEN i, const char *list[], const struct odbc_lookup_int *table, odbc_parser *parser)
     125             : {
     126          32 :         for (;*list; ++list) {
     127        4334 :                 if (i == lookup(*list, table, parser))
     128             :                         return true;
     129             :         }
     130             :         return false;
     131             : }
     132             : 
     133             : static void
     134           0 : print_values(FILE* f, const char *list[])
     135             : {
     136           0 :         const char *sep = "[";
     137           0 :         for (;*list; ++list, sep = ", ")
     138           0 :                 fprintf(f, "%s%s", sep, *list);
     139           0 :         fprintf(f, "]\n");
     140           0 : }
     141             : 
     142             : static void
     143        4436 : check_attr_ird(ATTR_PARAMS)
     144             : {
     145             :         SQLLEN i;
     146             :         SQLRETURN ret;
     147             : 
     148        4436 :         if (attr->type == type_CHARP) {
     149             :                 char buf[256];
     150             :                 SQLSMALLINT len;
     151             : 
     152         144 :                 ret = SQLColAttribute(odbc_stmt, 1, attr->value, buf, sizeof(buf), &len, NULL);
     153         144 :                 if (!SQL_SUCCEEDED(ret))
     154           0 :                         odbc_fatal(parser, ": failure not expected\n");
     155         144 :                 buf[sizeof(buf)-1] = 0;
     156         216 :                 if (!is_contained(C((SQLTCHAR*) buf), expected_values)) {
     157           0 :                         g_result = 1;
     158           0 :                         fprintf(stderr, "Line %u: invalid %s got %s expected ", odbc_line_num(parser), attr->name, buf);
     159           0 :                         print_values(stderr, expected_values);
     160             :                 }
     161             :                 return;
     162             :         }
     163             : 
     164        4292 :         i = 0xdeadbeef;
     165        4292 :         ret = SQLColAttribute(odbc_stmt, 1, attr->value, NULL, SQL_IS_INTEGER, NULL, &i);
     166        4292 :         if (!SQL_SUCCEEDED(ret))
     167           0 :                 odbc_fatal(parser, ": failure not expected\n");
     168             :         /* SQL_DESC_LENGTH is the same of SQLDescribeCol len */
     169        4292 :         if (attr->value == SQL_DESC_LENGTH) {
     170             :                 SQLSMALLINT scale, si;
     171             :                 SQLULEN prec;
     172         516 :                 CHKDescribeCol(1, NULL, 0, NULL, &si, &prec, &scale, &si, "S");
     173         516 :                 if (i != prec)
     174           0 :                         odbc_fatal(parser, ": attr %s SQLDescribeCol len %ld != SQLColAttribute len %ld\n",
     175             :                                    attr->name, (long) prec, (long) i);
     176             :         }
     177        4292 :         if (attr->value == SQL_DESC_SCALE) {
     178             :                 SQLSMALLINT scale, si;
     179             :                 SQLULEN prec;
     180         516 :                 CHKDescribeCol(1, NULL, 0, NULL, &si, &prec, &scale, &si, "S");
     181         516 :                 if (i != scale)
     182           0 :                         odbc_fatal(parser, ": attr %s SQLDescribeCol scale %ld != SQLColAttribute len %ld\n",
     183             :                                    attr->name, (long) scale, (long) i);
     184             :         }
     185        8584 :         if (!is_contained_lookup(i, expected_values, attr->lookup, parser)) {
     186           0 :                 g_result = 1;
     187           0 :                 fprintf(stderr, "Line %u: invalid %s got %s expected ", odbc_line_num(parser), attr->name, unlookup(i, attr->lookup));
     188           0 :                 print_values(stderr, expected_values);
     189             :         }
     190             : }
     191             : 
     192             : static void
     193          10 : check_attr_ard(ATTR_PARAMS)
     194             : {
     195             :         SQLINTEGER i, ind;
     196             :         SQLSMALLINT si;
     197             :         SQLLEN li;
     198             :         SQLRETURN ret;
     199          10 :         SQLHDESC desc = SQL_NULL_HDESC;
     200             :         char buf[256];
     201             : 
     202             :         /* get ARD */
     203          10 :         SQLGetStmtAttr(odbc_stmt, SQL_ATTR_APP_ROW_DESC, &desc, sizeof(desc), &ind);
     204             : 
     205          10 :         ret = SQL_ERROR;
     206          10 :         switch (attr->type) {
     207           0 :         case type_INTEGER:
     208           0 :                 i = 0xdeadbeef;
     209           0 :                 ret = SQLGetDescField(desc, 1, attr->value, (SQLPOINTER) & i, sizeof(SQLINTEGER), &ind);
     210           0 :                 li = i;
     211           0 :                 break;
     212          10 :         case type_SMALLINT:
     213          10 :                 si = 0xbeef;
     214          10 :                 ret = SQLGetDescField(desc, 1, attr->value, (SQLPOINTER) & si, sizeof(SQLSMALLINT), &ind);
     215          10 :                 li = si;
     216          10 :                 break;
     217           0 :         case type_LEN:
     218           0 :                 li = 0xdeadbeef;
     219           0 :                 ret = SQLGetDescField(desc, 1, attr->value, (SQLPOINTER) & li, sizeof(SQLLEN), &ind);
     220           0 :                 break;
     221           0 :         case type_CHARP:
     222           0 :                 ret = SQLGetDescField(desc, 1, attr->value, buf, sizeof(buf), &ind);
     223           0 :                 if (!SQL_SUCCEEDED(ret))
     224           0 :                         odbc_fatal(parser, ": failure not expected\n");
     225           0 :                 if (!is_contained(C((SQLTCHAR*) buf), expected_values)) {
     226           0 :                         g_result = 1;
     227           0 :                         fprintf(stderr, "Line %u: invalid %s got %s expected ", odbc_line_num(parser), attr->name, buf);
     228           0 :                         print_values(stderr, expected_values);
     229             :                 }
     230           0 :                 return;
     231             :         }
     232          10 :         if (!SQL_SUCCEEDED(ret))
     233           0 :                 odbc_fatal(parser, ": failure not expected\n");
     234          20 :         if (!is_contained_lookup(li, expected_values, attr->lookup, parser)) {
     235           0 :                 g_result = 1;
     236           0 :                 fprintf(stderr, "Line %u: invalid %s got %s expected ", odbc_line_num(parser), attr->name,
     237             :                         unlookup(li, attr->lookup));
     238           0 :                 print_values(stderr, expected_values);
     239             :         }
     240             : }
     241             : 
     242             : /* do not retry any attribute just return expected value so to make caller happy */
     243             : static void
     244         438 : check_attr_none(ATTR_PARAMS)
     245             : {
     246         438 : }
     247             : 
     248             : int
     249          10 : main(void)
     250             : {
     251          10 :         bool cond = true;
     252             : #define TEST_FILE "describecol.in"
     253          10 :         const char *in_file = FREETDS_SRCDIR "/" TEST_FILE;
     254             :         FILE *f;
     255             :         SQLINTEGER i;
     256             :         SQLLEN len;
     257          10 :         check_attr_t check_attr_p = check_attr_none;
     258             :         odbc_parser *parser;
     259             : 
     260          10 :         odbc_connect();
     261          10 :         odbc_command("SET TEXTSIZE 4096");
     262             : 
     263          10 :         SQLBindCol(odbc_stmt, 1, SQL_C_SLONG, &i, sizeof(i), &len);
     264             : 
     265          10 :         f = fopen(in_file, "r");
     266          10 :         if (!f)
     267           0 :                 f = fopen(TEST_FILE, "r");
     268          10 :         if (!f) {
     269           0 :                 fprintf(stderr, "error opening test file\n");
     270           0 :                 exit(1);
     271             :         }
     272             : 
     273             :         /* cache version */
     274          10 :         odbc_tds_version();
     275             : 
     276          10 :         parser = odbc_init_parser(f);
     277             :         for (;;) {
     278             :                 char *p;
     279             :                 const char *cmd;
     280             : 
     281        6850 :                 cmd = odbc_get_cmd_line(parser, &p, &cond);
     282        6850 :                 if (!cmd)
     283             :                         break;
     284             : 
     285        6840 :                 ODBC_FREE();
     286             : 
     287        6840 :                 if (strcmp(cmd, "odbc") == 0) {
     288          10 :                         int odbc3 = get_int(odbc_get_tok(&p), parser) == 3 ? 1 : 0;
     289             : 
     290        1312 :                         if (!cond) continue;
     291             : 
     292          10 :                         if (odbc_use_version3 != odbc3) {
     293          10 :                                 odbc_use_version3 = odbc3;
     294          10 :                                 odbc_disconnect();
     295          10 :                                 odbc_connect();
     296          10 :                                 odbc_command("SET TEXTSIZE 4096");
     297          10 :                                 SQLBindCol(odbc_stmt, 1, SQL_C_SLONG, &i, sizeof(i), &len);
     298             :                         }
     299             :                 }
     300             : 
     301             :                 /* select type */
     302        6840 :                 if (strcmp(cmd, "select") == 0) {
     303         700 :                         const char *type = odbc_get_str(parser, &p);
     304         700 :                         const char *value = odbc_get_str(parser, &p);
     305             :                         char sql[1024];
     306             : 
     307         876 :                         if (!cond) continue;
     308             : 
     309         574 :                         SQLMoreResults(odbc_stmt);
     310         574 :                         odbc_reset_statement();
     311             : 
     312         574 :                         snprintf(sql, sizeof(sql), "SELECT CONVERT(%s, %s) AS col", type, value);
     313             : 
     314             :                         /* ignore error, we only need precision of known types */
     315         574 :                         check_attr_p = check_attr_none;
     316         574 :                         if (odbc_command_with_result(odbc_stmt, sql) != SQL_SUCCESS) {
     317          50 :                                 odbc_reset_statement();
     318          50 :                                 SQLBindCol(odbc_stmt, 1, SQL_C_SLONG, &i, sizeof(i), &len);
     319          50 :                                 continue;
     320             :                         }
     321             : 
     322         524 :                         CHKFetch("SI");
     323         524 :                         SQLBindCol(odbc_stmt, 1, SQL_C_SLONG, &i, sizeof(i), &len);
     324         524 :                         check_attr_p = check_attr_ird;
     325             :                 }
     326             : 
     327             :                 /* set attribute */
     328        6664 :                 if (strcmp(cmd, "set") == 0) {
     329         280 :                         const struct attribute *attr = lookup_attr(odbc_get_tok(&p), parser);
     330         280 :                         const char *value = odbc_get_str(parser, &p);
     331             :                         SQLHDESC desc;
     332             :                         SQLRETURN ret;
     333             :                         SQLINTEGER ind;
     334             : 
     335         280 :                         if (!value)
     336           0 :                                 odbc_fatal(parser, ": value not defined\n");
     337             : 
     338         280 :                         if (!cond) continue;
     339             : 
     340             :                         /* get ARD */
     341         120 :                         SQLGetStmtAttr(odbc_stmt, SQL_ATTR_APP_ROW_DESC, &desc, sizeof(desc), &ind);
     342             : 
     343         120 :                         ret = SQL_ERROR;
     344         120 :                         switch (attr->type) {
     345           0 :                         case type_INTEGER:
     346           0 :                                 ret = SQLSetDescField(desc, 1, attr->value, TDS_INT2PTR(lookup(value, attr->lookup, parser)),
     347             :                                                       sizeof(SQLINTEGER));
     348           0 :                                 break;
     349         120 :                         case type_SMALLINT:
     350         120 :                                 ret = SQLSetDescField(desc, 1, attr->value, TDS_INT2PTR(lookup(value, attr->lookup, parser)),
     351             :                                                       sizeof(SQLSMALLINT));
     352         120 :                                 break;
     353           0 :                         case type_LEN:
     354           0 :                                 ret = SQLSetDescField(desc, 1, attr->value, TDS_INT2PTR(lookup(value, attr->lookup, parser)),
     355             :                                                       sizeof(SQLLEN));
     356           0 :                                 break;
     357           0 :                         case type_CHARP:
     358           0 :                                 ret = SQLSetDescField(desc, 1, attr->value, (SQLPOINTER) value, SQL_NTS);
     359           0 :                                 break;
     360             :                         }
     361         120 :                         if (!SQL_SUCCEEDED(ret))
     362           0 :                                 odbc_fatal(parser, ": failure not expected setting ARD attribute\n");
     363         120 :                         check_attr_p = check_attr_ard;
     364             :                 }
     365             : 
     366             :                 /* test attribute */
     367        6504 :                 if (strcmp(cmd, "attr") == 0) {
     368        5850 :                         const struct attribute *attr = lookup_attr(odbc_get_tok(&p), parser);
     369             :                         const char *expected[10];
     370             :                         int i;
     371             : 
     372        5850 :                         expected[0] = odbc_get_str(parser, &p);
     373             : 
     374        5850 :                         if (!expected[0])
     375           0 :                                 odbc_fatal(parser, ": value not defined\n");
     376             : 
     377        5850 :                         if (!cond) continue;
     378             : 
     379          40 :                         for (i = 1; ;++i) {
     380        4964 :                                 if (i >= 10)
     381           0 :                                         odbc_fatal(parser, "Too many values in attribute\n");
     382        4924 :                                 p += strspn(p, " \t");
     383        4924 :                                 if (!*p) {
     384        4884 :                                         expected[i] = NULL;
     385             :                                         break;
     386             :                                 }
     387          40 :                                 expected[i] = odbc_get_str(parser, &p);
     388             :                         }
     389        4884 :                         check_attr_p(attr, expected, parser);
     390             :                 }
     391             :         }
     392             : 
     393          10 :         fclose(f);
     394          10 :         odbc_free_parser(parser);
     395          10 :         odbc_disconnect();
     396             : 
     397          10 :         printf("Done.\n");
     398          10 :         return g_result;
     399             : }

Generated by: LCOV version 1.13