1 /*
2 Copyright (c) 2010, 2021, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include "my_global.h"
26 #include <string.h> // not using namespaces yet
27 #include <stdio.h> // not using namespaces yet
28 #include <stdlib.h> // not using namespaces yet
29
30 #include <util/NdbTap.hpp>
31
32 #include "dbug_utils.hpp"
33 #include "decimal_utils.hpp"
34 #include "CharsetMap.hpp"
35
36 #include "my_sys.h"
37 #include "mysql.h"
38
39 // need two levels of macro substitution
40 #define STRINGIFY(x) #x
41 #define TOSTRING(x) STRINGIFY(x)
42
43 // return non-zero value on failed condition
44 // C99's __func__ not supported by some C++ compilers yet (Solaris)
45 #define CHECK(cond) \
46 do { \
47 if (!(cond)) { \
48 fflush(stdout); \
49 fprintf(stderr, "\n!!! failed check: " TOSTRING(cond) \
50 ", file: " __FILE__ \
51 ", line: " TOSTRING(__LINE__) \
52 ".\n"); \
53 fflush(stderr); \
54 return 1; \
55 } \
56 } while (0)
57
test_dbug_utils()58 int test_dbug_utils()
59 {
60 printf("\n==== DBUG Utilities ====\n");
61 const int DBUG_BUF_SIZE = 1024;
62 char buffer[DBUG_BUF_SIZE];
63
64 const char * s = "some initial string";
65 const char * const s0 = "";
66 s = dbugExplain(buffer, DBUG_BUF_SIZE);
67 CHECK(!s || !strcmp(s, s0));
68
69 s = dbugExplain(NULL, DBUG_BUF_SIZE);
70 CHECK(!s);
71
72 s = dbugExplain(buffer, 0);
73 CHECK(!s);
74
75 const char * const s1 = "t";
76 dbugSet(s1);
77 s = dbugExplain(buffer, DBUG_BUF_SIZE);
78 CHECK(!s || !strcmp(s, s1));
79
80 dbugSet(NULL);
81 s = dbugExplain(buffer, DBUG_BUF_SIZE);
82 CHECK(!s || !strcmp(s, s1));
83
84 const char * const s2 = "d,somename:o,/tmp/somepath";
85 dbugPush(s2);
86 s = dbugExplain(buffer, DBUG_BUF_SIZE);
87 CHECK(!s || !strcmp(s, s2));
88
89 dbugPush(NULL);
90 s = dbugExplain(buffer, DBUG_BUF_SIZE);
91 CHECK(!s || !strcmp(s, s2));
92
93 const char * const s3 = "d,a,b,c,x,y,z";
94 dbugPush(s3);
95 s = dbugExplain(buffer, DBUG_BUF_SIZE);
96 CHECK(!s || (strspn(s, s3) == strlen(s3))); // allow for different order
97
98 dbugPop();
99 s = dbugExplain(buffer, DBUG_BUF_SIZE);
100 CHECK(!s || !strcmp(s, s2));
101
102 dbugPop();
103 s = dbugExplain(buffer, DBUG_BUF_SIZE);
104 CHECK(!s || !strcmp(s, s1));
105
106 dbugPush(NULL);
107 s = dbugExplain(buffer, DBUG_BUF_SIZE);
108 CHECK(!s || !strcmp(s, s1));
109
110 dbugPop();
111 s = dbugExplain(buffer, DBUG_BUF_SIZE);
112 CHECK(!s || !strcmp(s, s0));
113
114 return 0;
115 }
116
test_decimal(const char * s,int prec,int scale,int expected_rv)117 int test_decimal(const char *s, int prec, int scale, int expected_rv)
118 {
119 char bin_buff[128], str_buff[128];
120 int r1, r2 = 0;
121
122 str_buff[0] = 0;
123
124 // cast: decimal_str2bin expects 'int' for size_t strlen()
125 r1 = decimal_str2bin(s, (int)strlen(s), prec, scale, bin_buff, 128);
126 if(r1 <= E_DEC_OVERFLOW) {
127 r2 = decimal_bin2str(bin_buff, 128, prec, scale, str_buff, 128);
128 CHECK(r2 == E_DEC_OK);
129 }
130 printf("[%-2d,%-2d] %-29s => res=%d,%d %s\n",
131 prec, scale, s, r1, r2, str_buff);
132
133 if(r1 != expected_rv)
134 printf("decimal_str2bin returned %d when %d was expected.\n",
135 r1, expected_rv);
136 CHECK(r1 == expected_rv);
137
138 return 0;
139 }
140
test_decimal_conv()141 int test_decimal_conv()
142 {
143 printf("\n==== decimal_str2bin() / decimal_bin2str() ====\n");
144 CHECK(test_decimal("100", 3, -1, E_DEC_BAD_SCALE) == 0);
145 CHECK(test_decimal("3.3", 2, 1, E_DEC_OK) == 0);
146 CHECK(test_decimal("124.000", 20, 4, E_DEC_OK) == 0);
147 CHECK(test_decimal("-11", 14, 1, E_DEC_OK) == 0);
148 CHECK(test_decimal("1.123456000000000", 20, 16, E_DEC_OK) == 0);
149 CHECK(test_decimal("-20.333", 4, 2, E_DEC_TRUNCATED) == 0);
150 CHECK(test_decimal("0", 20, 10, E_DEC_OK) == 0);
151 CHECK(test_decimal("1 ", 20, 10, E_DEC_OK) == 0);
152 CHECK(test_decimal("1,35", 20, 10, E_DEC_OK) == 0);
153 CHECK(test_decimal("text", 20, 10, E_DEC_BAD_NUM) == 0);
154
155 return 0;
156 }
157
test_charset_map()158 int test_charset_map()
159 {
160 printf("\n==== CharsetMap ====\n");
161 printf("init MySQL lib, CharsetMap...\n");
162 my_init();
163 CharsetMap::init();
164
165 /* CharsetMap */
166 CharsetMap csmap;
167 int utf8_num = csmap.getUTF8CharsetNumber();
168 int utf16_num = csmap.getUTF16CharsetNumber();
169
170 /* If this mysql build does not include UTF-8 and either UCS-2 or UTF-16
171 then the test suite must fail.
172 */
173 printf("UTF-8 charset num: %d UTF-16 or UCS-2 charset num: %d\n",
174 utf8_num, utf16_num);
175 CHECK(utf8_num != 0);
176 CHECK(utf16_num != 0);
177
178 /* test csmap.getName()
179 */
180 const char *utf8 = csmap.getName(utf8_num);
181 CHECK(!strcmp(utf8,"UTF-8"));
182
183 /* MySQL 5.1 and earlier will have UCS-2 but later versions may have true
184 UTF-16. For information, print whether UTF-16 or UCS-2 is being used.
185 */
186 const char *utf16 = csmap.getMysqlName(csmap.getUTF16CharsetNumber());
187 printf("Using mysql's %s for UTF-16.\n", utf16);
188
189 /* Now we're going to recode.
190 We test with the string "ülker", which begins with the character
191 LATIN SMALL LETTER U WITH DIARESIS - unicode code point U+00FC.
192 In the latin1 encoding this is a literal 0xFC,
193 but in the UTF-8 representation it is 0xC3 0xBC.
194 */
195 // use numeric escape sequencesx.. (or downcast integer literal to char)
196 // to avoid narrowing conversion compile warnings
197 const char my_word_latin1[6] = { '\xFC', 'l', 'k', 'e', 'r', 0};
198 const char my_word_utf8[7] = { '\xC3', '\xBC', 'l', 'k', 'e', 'r', 0};
199 const char my_word_truncated[5] = { '\xC3', '\xBC', 'l', 'k', 0};
200 // no need for 'unsigned char[]'
201 const char my_bad_utf8[5] = { 'l', '\xBC', 'a', 'd', 0};
202 char result_buff_1[32];
203 char result_buff_2[32];
204 char result_buff_too_small[4];
205 int lengths[2];
206
207 /* latin1 must be available to run the recode test */
208 int latin1_num = csmap.getCharsetNumber("latin1");
209 printf("latin1 charset number: %d standard name: \"%s\" \n",
210 latin1_num, csmap.getName(latin1_num));
211 CHECK(latin1_num != 0);
212 CHECK(! strcmp(csmap.getName(latin1_num), "windows-1252"));
213
214 printf("Latin1: \"%s\" UTF8: \"%s\" \n",
215 my_word_latin1, my_word_utf8);
216
217 /* RECODE TEST 1: recode from UTF-8 to Latin 1 */
218 lengths[0] = 7;
219 lengths[1] = 32;
220 CharsetMap::RecodeStatus rr1 = csmap.recode(lengths, utf8_num, latin1_num,
221 my_word_utf8, result_buff_1);
222 printf("Recode Test 1 - UTF-8 to Latin-1: %d %d %d \"%s\" => \"%s\" \n",
223 rr1, lengths[0], lengths[1], my_word_utf8, result_buff_1);
224 CHECK(rr1 == CharsetMap::RECODE_OK);
225 CHECK(lengths[0] == 7);
226 CHECK(lengths[1] == 6);
227 CHECK(!strcmp(result_buff_1, my_word_latin1));
228
229 /* RECODE TEST 2: recode from Latin1 to to UTF-8 */
230 lengths[0] = 6;
231 lengths[1] = 32;
232 CharsetMap::RecodeStatus rr2 = csmap.recode(lengths, latin1_num, utf8_num,
233 my_word_latin1, result_buff_2);
234 printf("Recode Test 2 - Latin-1 to UTF-8: %d %d %d \"%s\" => \"%s\" \n",
235 rr2, lengths[0], lengths[1], my_word_latin1, result_buff_2);
236 CHECK(rr2 == CharsetMap::RECODE_OK);
237 CHECK(lengths[0] == 6);
238 CHECK(lengths[1] == 7);
239 CHECK(!(strcmp(result_buff_2, my_word_utf8)));
240
241 /* RECODE TEST 3: recode with a too-small result buffer */
242 lengths[0] = 6;
243 lengths[1] = 4;
244 CharsetMap::RecodeStatus rr3 = csmap.recode(lengths, latin1_num, utf8_num,
245 my_word_latin1, result_buff_too_small);
246 printf("Recode Test 3 - too-small buffer: %d %d %d \"%s\" => \"%s\" \n",
247 rr3, lengths[0], lengths[1], my_word_latin1, result_buff_too_small);
248 CHECK(rr3 == CharsetMap::RECODE_BUFF_TOO_SMALL);
249 CHECK(lengths[0] == 3);
250 CHECK(lengths[1] == 4);
251 /* Confirm that the first four characters were indeed recoded: */
252 CHECK(!(strncmp(result_buff_too_small, my_word_truncated, 4)));
253
254 /* RECODE TEST 4: recode with an invalid character set */
255 CharsetMap::RecodeStatus rr4 = csmap.recode(lengths, 0, 999, my_word_latin1, result_buff_2);
256 printf("Recode Test 4 - invalid charset: %d \n", rr4);
257 CHECK(rr4 == CharsetMap::RECODE_BAD_CHARSET);
258
259 /* RECODE TEST 5: source string is ill-formed UTF-8 */
260 lengths[0] = 5;
261 lengths[1] = 32;
262 int rr5 = csmap.recode(lengths, utf8_num, latin1_num,
263 my_bad_utf8, result_buff_2);
264 printf("Recode Test 5 - ill-formed source string: %d \n", rr5);
265 CHECK(rr5 == CharsetMap::RECODE_BAD_SRC);
266
267
268 printf("isMultibyte TEST: ");
269 const bool * result1, * result2, * result3;
270 result1 = csmap.isMultibyte(latin1_num);
271 result2 = csmap.isMultibyte(utf16_num);
272 result3 = csmap.isMultibyte(utf8_num);
273 printf("latin 1: %s UTF16: %s UTF8: %s\n",
274 *result1 ? "Yes" : "No",
275 *result2 ? "Yes" : "No",
276 *result3 ? "Yes" : "No");
277 CHECK(! *result1);
278 CHECK(*result2);
279 CHECK(*result3);
280
281 int nNull = 0, nSingle = 0, nMulti = 0;
282 for(int i = 0 ; i < 256 ; i++) {
283 const bool *r = csmap.isMultibyte(i);
284 if(r) {
285 if(*r) nMulti++;
286 else nSingle++;
287 }
288 else nNull++;
289 }
290 printf("Charset stats: %d unused, %d single-byte, %d multi-byte\n",
291 nNull, nSingle, nMulti);
292 // If there is not at least one of each, then something is probably wrong
293 CHECK(nNull && nSingle && nMulti);
294
295 printf("unload CharsetMap...\n");
296 CharsetMap::unload();
297
298 return 0;
299 }
300
main(int argc,const char ** argv)301 int main(int argc, const char** argv)
302 {
303 // TAP: print number of tests to run
304 plan(3);
305
306 // init MySQL lib
307 if (my_init())
308 BAIL_OUT("my_init() failed");
309
310 // TAP: report test result: ok(passed, non-null format string)
311 ok(test_dbug_utils() == 0, "subtest: dbug_utils");
312 ok(test_decimal_conv() == 0, "subtest: decimal_conv");
313 ok(test_charset_map() == 0, "subtest: charset_map");
314
315 // TAP: print summary report and return exit status
316 return exit_status();
317 }
318