LCOV - code coverage report
Current view: top level - src/tds/unittests - convert_bounds.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 87 95 91.6 %
Date: 2025-01-18 12:13:41 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
       2             :  * Copyright (C) 2022  Frediano Ziglio
       3             :  *
       4             :  * This library is free software; you can redistribute it and/or
       5             :  * modify it under the terms of the GNU Library General Public
       6             :  * License as published by the Free Software Foundation; either
       7             :  * version 2 of the License, or (at your option) any later version.
       8             :  *
       9             :  * This library is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             :  * Library General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU Library General Public
      15             :  * License along with this library; if not, write to the
      16             :  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      17             :  * Boston, MA 02111-1307, USA.
      18             :  */
      19             : 
      20             : /* 
      21             :  * Purpose: test conversion bounds of integers and floating points.
      22             :  */
      23             : #include "common.h"
      24             : #include <assert.h>
      25             : #include <freetds/convert.h>
      26             : #include <freetds/utils/smp.h>
      27             : 
      28             : static TDS_INT convert_and_free(int srctype, const void *src, TDS_UINT srclen, int desttype, CONV_RESULT *cr);
      29             : static bool is_valid(const char *num, int type, CONV_RESULT *cr);
      30             : static double convert_to_float(smp n, int type);
      31             : static int64_t get_float_precision_factor(smp n, int type);
      32             : static void real_test(smp n, int type, bool is_integer);
      33             : static void double_to_string(char *out, double d);
      34             : 
      35             : static TDSCONTEXT *ctx;
      36             : 
      37             : typedef struct {
      38             :         int type;
      39             :         bool is_integer;
      40             : } type_desc;
      41             : 
      42             : /* list of integer and floating point types */
      43             : static const type_desc number_types[] = {
      44             :         { SYBINT1, true },
      45             :         { SYBUINT1, true },
      46             :         { SYBINT2, true },
      47             :         { SYBUINT2, true },
      48             :         { SYBINT4, true },
      49             :         { SYBUINT4, true },
      50             :         { SYBINT8, true },
      51             :         { SYBUINT8, true },
      52             :         { SYBMONEY4, true },
      53             :         { SYBMONEY, true },
      54             :         { SYBREAL, false },
      55             :         { SYBFLT8, false },
      56             :         { SYBNUMERIC, true },
      57             :         { 0, false },
      58             : };
      59             : 
      60             : /* list of upper and lower bounds for numbers */
      61             : static const char *bounds[] = {
      62             :         "0",
      63             :         /* 8 bits */
      64             :         "128",
      65             :         "256",
      66             :         /* 16 bits */
      67             :         "0x8000",
      68             :         "0x1'0000",
      69             :         /* money */
      70             :         "214748",
      71             :         "922337203685477",
      72             :         /* 32 bits */
      73             :         "0x8000'0000",
      74             :         "0x1'0000'0000",
      75             :         /* 64 bits */
      76             :         "0x8000'0000'0000'0000",
      77             :         "0x1'0000'0000'0000'0000",
      78             :         NULL,
      79             : };
      80             : 
      81             : int
      82           8 : main(int argc, char **argv)
      83             : {
      84             :         const char **bound;
      85             : 
      86           8 :         setbuf(stdout, NULL);
      87           8 :         setbuf(stderr, NULL);
      88             : 
      89           8 :         ctx = tds_alloc_context(NULL);
      90           8 :         assert(ctx);
      91           8 :         if (!ctx->locale->datetime_fmt) {
      92             :                 /* set default in case there's no locale file */
      93           0 :                 ctx->locale->datetime_fmt = strdup(STD_DATETIME_FMT);
      94             :         }
      95             : 
      96             :         /* test all bounds for all types */
      97          88 :         for (bound = bounds; *bound; ++bound) {
      98             :                 const type_desc *t;
      99          88 :                 smp n = smp_from_string(*bound);
     100             : 
     101        1232 :                 for (t = number_types; t->type != 0; ++t)
     102        1144 :                         real_test(n, t->type, t->is_integer);
     103             :         }
     104             : 
     105           8 :         tds_free_context(ctx);
     106             :         return 0;
     107             : }
     108             : 
     109             : static TDS_INT
     110     1258400 : convert_and_free(int srctype, const void *src, TDS_UINT srclen, int desttype, CONV_RESULT *cr)
     111             : {
     112             :         TDS_INT res;
     113             : 
     114     1258400 :         cr->n.precision = 20;
     115     1258400 :         cr->n.scale = 0;
     116             : 
     117     1258400 :         res = tds_convert(ctx, srctype, src, srclen, desttype, cr);
     118     1258400 :         if (res < 0)
     119             :                 return res;
     120             : 
     121      750696 :         switch (desttype) {
     122           0 :         case SYBCHAR: case SYBVARCHAR: case SYBTEXT: case XSYBCHAR: case XSYBVARCHAR:
     123             :         case SYBBINARY: case SYBVARBINARY: case SYBIMAGE: case XSYBBINARY: case XSYBVARBINARY:
     124             :         case SYBLONGBINARY:
     125           0 :                 free(cr->c);
     126           0 :                 break;
     127             :         }
     128             :         return res;
     129             : }
     130             : 
     131             : static void
     132        1144 : real_test(smp n, int srctype, bool is_integer)
     133             : {
     134             :         int i;
     135             : 
     136             :         /* test both positive and negative */
     137        3240 :         for (i = 0; i < 2; ++i) {
     138             :                 const type_desc *t;
     139        2192 :                 char *s_num = smp_to_string(n);
     140             :                 int diff;
     141        2192 :                 int64_t precision_factor = 1;
     142             : 
     143        2192 :                 printf("Testing conversions from %s for number %s\n", tds_prtype(srctype), s_num);
     144        2192 :                 free(s_num);
     145             : 
     146        2192 :                 if (!is_integer)
     147         336 :                         precision_factor = get_float_precision_factor(n, srctype);
     148             : 
     149             :                 /* test bound conversions to all number types */
     150       30688 :                 for (t = number_types; t->type != 0; ++t) {
     151      598416 :                         for (diff = -10; diff <= 10; ++diff) {
     152             :                                 bool valid_src, valid_dest;
     153      598416 :                                 int desttype = t->type;
     154             :                                 CONV_RESULT cr_src, cr_dest;
     155             :                                 int result;
     156      598416 :                                 smp num = smp_add(n, smp_from_int(diff * precision_factor));
     157             : 
     158             :                                 /* convert from char to check for validity */
     159      598416 :                                 s_num = smp_to_string(num);
     160      598416 :                                 valid_src = is_valid(s_num, srctype, &cr_src);
     161             : 
     162             :                                 /* if we were not able to get the source number do not check conversion */
     163      598416 :                                 if (!valid_src) {
     164      268424 :                                         TDS_ZERO_FREE(s_num);
     165      268424 :                                         continue;
     166             :                                 }
     167             : 
     168             :                                 /* NUMERIC has a special encoding for -0 number */
     169      329992 :                                 if (srctype == SYBNUMERIC && i > 0 && smp_is_zero(num))
     170         104 :                                         cr_src.n.array[0] = 1;
     171             : 
     172      329992 :                                 if (is_integer) {
     173      238264 :                                         valid_dest = is_valid(s_num, desttype, NULL);
     174             :                                 } else {
     175             :                                         /* in order to account for possible lost of precision convert from
     176             :                                          * conversion result */
     177       91728 :                                         double d = convert_to_float(smp_from_string(s_num), srctype);
     178             :                                         char out_n[128];
     179             : 
     180       91728 :                                         double_to_string(out_n, d);
     181       91728 :                                         valid_dest = is_valid(out_n, desttype, NULL);
     182             :                                 }
     183             : 
     184             :                                 /* try to convert */
     185      329992 :                                 result = convert_and_free(srctype, &cr_src, 8, desttype, &cr_dest);
     186             : 
     187             :                                 /* conversion should succeed if previously succeeded or viceversa */
     188      329992 :                                 if (valid_dest != (result >= 0)) {
     189           0 :                                         fprintf(stderr, "Unmatch results from %s to %s for %s\n"
     190             :                                                 "results %d (from string) %d (from source type)\n",
     191             :                                                 tds_prtype(srctype), tds_prtype(desttype), s_num,
     192             :                                                 valid_dest, (result >= 0));
     193           0 :                                         TDS_ZERO_FREE(s_num);
     194           0 :                                         exit(1);
     195             :                                 }
     196             : 
     197             :                                 /* if failed it should have been an overflow, types are compatible */
     198      329992 :                                 assert((result >= 0) || result == TDS_CONVERT_OVERFLOW);
     199      329992 :                                 TDS_ZERO_FREE(s_num);
     200             :                         }
     201             :                 }
     202             : 
     203        2192 :                 if (smp_is_zero(n) && srctype != SYBNUMERIC)
     204             :                         break;
     205        2096 :                 n = smp_negate(n);
     206             :         }
     207        1144 : }
     208             : 
     209             : /* check if a number is valid converted to some type */
     210             : static bool
     211      928408 : is_valid(const char *num, int type, CONV_RESULT *cr)
     212             : {
     213             :         CONV_RESULT dummy_cr;
     214             : 
     215      928408 :         if (!cr)
     216      329992 :                 cr = &dummy_cr;
     217             : 
     218      928408 :         return convert_and_free(SYBVARCHAR, num, strlen(num), type, cr) >= 0;
     219             : }
     220             : 
     221             : /* convert multiple precision to a floating number of a specific type */
     222             : static double
     223       95152 : convert_to_float(smp n, int type)
     224             : {
     225       95152 :         double ret = smp_to_double(n);
     226             : 
     227       95152 :         switch (type) {
     228       48400 :         case SYBREAL:
     229       48400 :                 return (TDS_REAL) ret;
     230             :         case SYBFLT8:
     231             :                 return (TDS_FLOAT) ret;
     232             :         default:
     233           0 :                 assert(!"wrong type");
     234             :         }
     235             :         return 0;
     236             : }
     237             : 
     238             : /* Compute a factor in order to see the change to the original number expressed
     239             :  * with a given floating point number.
     240             :  * This is due to the limited precision of a floating point type.
     241             :  * If we use numbers with 20 digits but the precision is just 10 we need
     242             :  * increment in the order of 10 digits to see changes in the floating point
     243             :  * numbers if we increment them.
     244             :  */
     245             : static int64_t
     246         336 : get_float_precision_factor(smp n, int type)
     247             : {
     248             :         int shift;
     249         336 :         const double orig = convert_to_float(n, type);
     250        2688 :         for (shift = 0; ; ++shift) {
     251        2688 :                 smp diff = smp_from_int(((int64_t) 1) << shift);
     252             : 
     253             :                 /* returns only if both n+diff and n-diff change the original number */
     254        3088 :                 if (orig != convert_to_float(smp_add(n, diff), type) &&
     255         400 :                     orig != convert_to_float(smp_sub(n, diff), type))
     256             :                         break;
     257             :         }
     258         336 :         return ((int64_t) 1) << (shift ? shift - 1 : shift);
     259             : }
     260             : 
     261             : /* Convert a double to a string.
     262             :  * This is done to avoid a bug in some old versions of Visual Studio (maybe CRT).
     263             :  * It's assumed that the double contains a number with no digits after the unit.
     264             :  */
     265             : static void
     266       91728 : double_to_string(char *out, double d)
     267             : {
     268       91728 :         int bdigits = 0;
     269       91728 :         double exp = 1.f;
     270       91728 :         bool negative = false;
     271             :         char *s;
     272       91728 :         smp n = smp_zero;
     273             : 
     274       91728 :         if (d < 0) {
     275       45760 :                 negative = true;
     276       45760 :                 d = -d;
     277             :         }
     278     2796144 :         while (exp <= d) {
     279     2704416 :                 exp *= 2.f;
     280     2704416 :                 ++bdigits;
     281             :         }
     282     2796144 :         for (; bdigits >= 0; --bdigits) {
     283     2796144 :                 n = smp_add(n,n);
     284     2796144 :                 if (exp <= d) {
     285     1038752 :                         n = smp_add(n, smp_one);
     286     1038752 :                         d -= exp;
     287             :                 }
     288     2796144 :                 exp *= 0.5f;
     289             :         }
     290       91728 :         if (negative)
     291       45760 :                 n = smp_negate(n);
     292       91728 :         s = smp_to_string(n);
     293       91728 :         strcpy(out, s);
     294       91728 :         free(s);
     295       91728 : }

Generated by: LCOV version 1.13