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 : { SYBSINT1, true },
46 : { SYBUINT1, true },
47 : { SYBINT2, true },
48 : { SYBUINT2, true },
49 : { SYBINT4, true },
50 : { SYBUINT4, true },
51 : { SYBINT8, true },
52 : { SYBUINT8, true },
53 : { SYBMONEY4, true },
54 : { SYBMONEY, true },
55 : { SYBREAL, false },
56 : { SYBFLT8, false },
57 : { SYBNUMERIC, true },
58 : { 0, false },
59 : };
60 :
61 : /* list of upper and lower bounds for numbers */
62 : static const char *bounds[] = {
63 : "0",
64 : /* 8 bits */
65 : "128",
66 : "256",
67 : /* 16 bits */
68 : "0x8000",
69 : "0x1'0000",
70 : /* money */
71 : "214748",
72 : "922337203685477",
73 : /* 32 bits */
74 : "0x8000'0000",
75 : "0x1'0000'0000",
76 : /* 64 bits */
77 : "0x8000'0000'0000'0000",
78 : "0x1'0000'0000'0000'0000",
79 : NULL,
80 : };
81 :
82 : int
83 8 : main(void)
84 : {
85 : const char **bound;
86 :
87 8 : setbuf(stdout, NULL);
88 8 : setbuf(stderr, NULL);
89 :
90 8 : ctx = tds_alloc_context(NULL);
91 8 : assert(ctx);
92 8 : if (!ctx->locale->datetime_fmt) {
93 : /* set default in case there's no locale file */
94 0 : ctx->locale->datetime_fmt = strdup(STD_DATETIME_FMT);
95 : }
96 :
97 : /* test all bounds for all types */
98 88 : for (bound = bounds; *bound; ++bound) {
99 : const type_desc *t;
100 88 : smp n = smp_from_string(*bound);
101 :
102 1320 : for (t = number_types; t->type != 0; ++t)
103 1232 : real_test(n, t->type, t->is_integer);
104 : }
105 :
106 8 : tds_free_context(ctx);
107 : return 0;
108 : }
109 :
110 : static TDS_INT
111 1414000 : convert_and_free(int srctype, const void *src, TDS_UINT srclen, int desttype, CONV_RESULT *cr)
112 : {
113 : TDS_INT res;
114 :
115 1414000 : cr->n.precision = 20;
116 1414000 : cr->n.scale = 0;
117 :
118 1414000 : res = tds_convert(ctx, srctype, src, srclen, desttype, cr);
119 1414000 : if (res < 0)
120 : return res;
121 :
122 795904 : switch (desttype) {
123 0 : case SYBCHAR: case SYBVARCHAR: case SYBTEXT: case XSYBCHAR: case XSYBVARCHAR:
124 : case SYBBINARY: case SYBVARBINARY: case SYBIMAGE: case XSYBBINARY: case XSYBVARBINARY:
125 : case SYBLONGBINARY:
126 0 : free(cr->c);
127 0 : break;
128 : }
129 : return res;
130 : }
131 :
132 : static void
133 1232 : real_test(smp n, int srctype, bool is_integer)
134 : {
135 : int i;
136 :
137 : /* test both positive and negative */
138 3488 : for (i = 0; i < 2; ++i) {
139 : const type_desc *t;
140 2360 : char *s_num = smp_to_string(n);
141 : int diff;
142 2360 : int64_t precision_factor = 1;
143 :
144 2360 : printf("Testing conversions from %s for number %s\n", tds_prtype(srctype), s_num);
145 2360 : free(s_num);
146 :
147 2360 : if (!is_integer)
148 336 : precision_factor = get_float_precision_factor(n, srctype);
149 :
150 : /* test bound conversions to all number types */
151 35400 : for (t = number_types; t->type != 0; ++t) {
152 693840 : for (diff = -10; diff <= 10; ++diff) {
153 : bool valid_src, valid_dest;
154 693840 : int desttype = t->type;
155 : CONV_RESULT cr_src, cr_dest;
156 : int result;
157 693840 : smp num = smp_add(n, smp_from_int(diff * precision_factor));
158 :
159 : /* convert from char to check for validity */
160 693840 : s_num = smp_to_string(num);
161 693840 : valid_src = is_valid(s_num, srctype, &cr_src);
162 :
163 : /* if we were not able to get the source number do not check conversion */
164 693840 : if (!valid_src) {
165 333760 : TDS_ZERO_FREE(s_num);
166 333760 : continue;
167 : }
168 :
169 : /* NUMERIC has a special encoding for -0 number */
170 360080 : if (srctype == SYBNUMERIC && i > 0 && smp_is_zero(num))
171 112 : cr_src.n.array[0] = 1;
172 :
173 360080 : if (is_integer) {
174 261296 : valid_dest = is_valid(s_num, desttype, NULL);
175 : } else {
176 : /* in order to account for possible lost of precision convert from
177 : * conversion result */
178 98784 : double d = convert_to_float(smp_from_string(s_num), srctype);
179 : char out_n[128];
180 :
181 98784 : double_to_string(out_n, d);
182 98784 : valid_dest = is_valid(out_n, desttype, NULL);
183 : }
184 :
185 : /* try to convert */
186 360080 : result = convert_and_free(srctype, &cr_src, 8, desttype, &cr_dest);
187 :
188 : /* conversion should succeed if previously succeeded or viceversa */
189 360080 : if (valid_dest != (result >= 0)) {
190 0 : fprintf(stderr, "Unmatch results from %s to %s for %s\n"
191 : "results %d (from string) %d (from source type)\n",
192 : tds_prtype(srctype), tds_prtype(desttype), s_num,
193 : valid_dest, (result >= 0));
194 0 : TDS_ZERO_FREE(s_num);
195 0 : exit(1);
196 : }
197 :
198 : /* if failed it should have been an overflow, types are compatible */
199 360080 : assert((result >= 0) || result == TDS_CONVERT_OVERFLOW);
200 360080 : TDS_ZERO_FREE(s_num);
201 : }
202 : }
203 :
204 2360 : if (smp_is_zero(n) && srctype != SYBNUMERIC)
205 : break;
206 2256 : n = smp_negate(n);
207 : }
208 1232 : }
209 :
210 : /* check if a number is valid converted to some type */
211 : static bool
212 1053920 : is_valid(const char *num, int type, CONV_RESULT *cr)
213 : {
214 : CONV_RESULT dummy_cr;
215 :
216 1053920 : if (!cr)
217 360080 : cr = &dummy_cr;
218 :
219 1053920 : return convert_and_free(SYBVARCHAR, num, strlen(num), type, cr) >= 0;
220 : }
221 :
222 : /* convert multiple precision to a floating number of a specific type */
223 : static double
224 102208 : convert_to_float(smp n, int type)
225 : {
226 102208 : double ret = smp_to_double(n);
227 :
228 102208 : switch (type) {
229 51928 : case SYBREAL:
230 51928 : return (TDS_REAL) ret;
231 : case SYBFLT8:
232 : return (TDS_FLOAT) ret;
233 : default:
234 0 : assert(!"wrong type");
235 : }
236 : return 0;
237 : }
238 :
239 : /* Compute a factor in order to see the change to the original number expressed
240 : * with a given floating point number.
241 : * This is due to the limited precision of a floating point type.
242 : * If we use numbers with 20 digits but the precision is just 10 we need
243 : * increment in the order of 10 digits to see changes in the floating point
244 : * numbers if we increment them.
245 : */
246 : static int64_t
247 336 : get_float_precision_factor(smp n, int type)
248 : {
249 : int shift;
250 336 : const double orig = convert_to_float(n, type);
251 2688 : for (shift = 0; ; ++shift) {
252 2688 : smp diff = smp_from_int(((int64_t) 1) << shift);
253 :
254 : /* returns only if both n+diff and n-diff change the original number */
255 3088 : if (orig != convert_to_float(smp_add(n, diff), type) &&
256 400 : orig != convert_to_float(smp_sub(n, diff), type))
257 : break;
258 : }
259 336 : return ((int64_t) 1) << (shift ? shift - 1 : shift);
260 : }
261 :
262 : /* Convert a double to a string.
263 : * This is done to avoid a bug in some old versions of Visual Studio (maybe CRT).
264 : * It's assumed that the double contains a number with no digits after the unit.
265 : */
266 : static void
267 98784 : double_to_string(char *out, double d)
268 : {
269 98784 : int bdigits = 0;
270 98784 : double exp = 1.f;
271 98784 : bool negative = false;
272 : char *s;
273 98784 : smp n = smp_zero;
274 :
275 98784 : if (d < 0) {
276 49280 : negative = true;
277 49280 : d = -d;
278 : }
279 3011232 : while (exp <= d) {
280 2912448 : exp *= 2.f;
281 2912448 : ++bdigits;
282 : }
283 3011232 : for (; bdigits >= 0; --bdigits) {
284 3011232 : n = smp_add(n,n);
285 3011232 : if (exp <= d) {
286 1118656 : n = smp_add(n, smp_one);
287 1118656 : d -= exp;
288 : }
289 3011232 : exp *= 0.5f;
290 : }
291 98784 : if (negative)
292 49280 : n = smp_negate(n);
293 98784 : s = smp_to_string(n);
294 98784 : strcpy(out, s);
295 98784 : free(s);
296 98784 : }
|