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 : }
|