1 /*
2 * Test cases for dealing with SQL_NUMERIC_STRUCT
3 */
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include "common.h"
8
9 static unsigned char
hex_to_int(char c)10 hex_to_int(char c)
11 {
12 char result;
13
14 if (c >= '0' && c <= '9')
15 result = c - '0';
16 else if (c >= 'a' && c <= 'f')
17 result = c - 'a' + 10;
18 else if (c >= 'A' && c <= 'F')
19 result = c - 'A' + 10;
20 else
21 {
22 fprintf(stderr, "invalid hex-encoded numeric value\n");
23 exit(1);
24 }
25 return (unsigned char) result;
26 }
27
28 static void
build_numeric_struct(SQL_NUMERIC_STRUCT * numericparam,unsigned char sign,char * hexstr,unsigned char precision,unsigned char scale)29 build_numeric_struct(SQL_NUMERIC_STRUCT *numericparam,
30 unsigned char sign, char *hexstr,
31 unsigned char precision, unsigned char scale)
32 {
33 int len;
34
35 /* parse the hex-encoded value */
36 memset(numericparam, 0, sizeof(SQL_NUMERIC_STRUCT));
37
38 numericparam->sign = sign;
39 numericparam->precision = precision;
40 numericparam->scale = scale;
41
42 len = 0;
43 while (*hexstr)
44 {
45 if (*hexstr == ' ')
46 {
47 hexstr++;
48 continue;
49 }
50 if (len >= SQL_MAX_NUMERIC_LEN)
51 {
52 fprintf(stderr, "hex-encoded numeric value too long\n");
53 exit(1);
54 }
55 numericparam->val[len] =
56 hex_to_int(*hexstr) << 4 | hex_to_int(*(hexstr + 1));
57 hexstr += 2;
58 len++;
59 }
60 }
61
62 static void
test_numeric_param(HSTMT hstmt,unsigned char sign,char * hexval,unsigned char precision,unsigned char scale)63 test_numeric_param(HSTMT hstmt, unsigned char sign, char *hexval,
64 unsigned char precision, unsigned char scale)
65 {
66 SQL_NUMERIC_STRUCT numericparam;
67 SQLLEN cbParam1;
68 SQLRETURN rc;
69 char buf[200];
70
71 build_numeric_struct(&numericparam, sign, hexval, precision, scale);
72
73 cbParam1 = sizeof(numericparam);
74 rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT,
75 SQL_C_NUMERIC, /* value type */
76 SQL_NUMERIC, /* param type */
77 0, /* column size (ignored for SQL_INTERVAL_SECOND) */
78 0, /* dec digits */
79 &numericparam, /* param value ptr */
80 sizeof(numericparam), /* buffer len (ignored for SQL_C_INTERVAL_SECOND) */
81 &cbParam1 /* StrLen_or_IndPtr (ignored for SQL_C_INTERVAL_SECOND) */);
82 CHECK_STMT_RESULT(rc, "SQLBindParameter failed", hstmt);
83
84 /* Execute */
85 rc = SQLExecute(hstmt);
86 if (!SQL_SUCCEEDED(rc))
87 {
88 print_diag("SQLExecute failed", SQL_HANDLE_STMT, hstmt);
89 }
90 else
91 {
92 /* print result */
93 rc = SQLFetch(hstmt);
94 CHECK_STMT_RESULT(rc, "SQLFetch failed", hstmt);
95
96 rc = SQLGetData(hstmt, 1, SQL_C_CHAR, buf, sizeof(buf), NULL);
97 CHECK_STMT_RESULT(rc, "SQLGetData failed", hstmt);
98 printf("sign %u prec %u scale %d val %s:\n %s\n",
99 sign, precision, scale, hexval, buf);
100 }
101
102 rc = SQLFreeStmt(hstmt, SQL_CLOSE);
103 CHECK_STMT_RESULT(rc, "SQLFreeStmt failed", hstmt);
104 }
105
106 static void
test_numeric_result(HSTMT hstmt,char * numstr)107 test_numeric_result(HSTMT hstmt, char *numstr)
108 {
109 char sql[100];
110 SQL_NUMERIC_STRUCT numericres;
111 SQLRETURN rc;
112
113 /*
114 * assume 'numstr' param to be well-formed (we're testing how the
115 * results come out, not the input handling)
116 */
117 snprintf(sql, sizeof(sql), "SELECT '%s'::numeric", numstr);
118 rc = SQLExecDirect(hstmt, (SQLCHAR *) sql, SQL_NTS);
119 CHECK_STMT_RESULT(rc, "SQLExecDirect failed", hstmt);
120
121 rc = SQLFetch(hstmt);
122 CHECK_STMT_RESULT(rc, "SQLFetch failed", hstmt);
123
124 rc = SQLGetData(hstmt, 1, SQL_C_NUMERIC, &numericres, sizeof(numericres), NULL);
125 CHECK_STMT_RESULT(rc, "SQLGetData failed", hstmt);
126 printf("%s:\n sign %u prec %u scale %d val %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
127 numstr, numericres.sign, numericres.precision, numericres.scale,
128 numericres.val[0], numericres.val[1],
129 numericres.val[2], numericres.val[3],
130 numericres.val[4], numericres.val[5],
131 numericres.val[6], numericres.val[7],
132 numericres.val[8], numericres.val[9],
133 numericres.val[10], numericres.val[11],
134 numericres.val[12], numericres.val[13],
135 numericres.val[14], numericres.val[15]);
136
137 rc = SQLFreeStmt(hstmt, SQL_CLOSE);
138 CHECK_STMT_RESULT(rc, "SQLFreeStmt failed", hstmt);
139 }
140
main(int argc,char ** argv)141 int main(int argc, char **argv)
142 {
143 SQLRETURN rc;
144 HSTMT hstmt = SQL_NULL_HSTMT;
145
146 test_connect();
147
148 rc = SQLAllocHandle(SQL_HANDLE_STMT, conn, &hstmt);
149 if (!SQL_SUCCEEDED(rc))
150 {
151 print_diag("failed to allocate stmt handle", SQL_HANDLE_DBC, conn);
152 exit(1);
153 }
154
155 /**** Test binding SQL_NUMERIC_STRUCT params (SQL_C_NUMERIC) ****/
156
157 printf("Testing SQL_NUMERIC_STRUCT params...\n\n");
158
159 rc = SQLPrepare(hstmt, (SQLCHAR *) "SELECT ?::numeric", SQL_NTS);
160 CHECK_STMT_RESULT(rc, "SQLPrepare failed", hstmt);
161
162 /* 25.212 (per Microsoft KB 22831) */
163 test_numeric_param(hstmt, 1, "7C62", 5, 3);
164
165 /* 24197857161011715162171839636988778104 */
166 test_numeric_param(hstmt, 1, "78563412 78563412 78563412 78563412", 41, 0);
167
168 /* 12345678901234567890123456789012345678 */
169 test_numeric_param(hstmt, 1, "4EF338DE509049C4133302F0F6B04909", 38, 0);
170
171 /* highest possible non-scaled: 340282366920938463463374607431768211455 */
172 test_numeric_param(hstmt, 1, "FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF", 50, 0);
173
174 /* positive and negative zero */
175 test_numeric_param(hstmt, 1, "00", 1, 0);
176 test_numeric_param(hstmt, 0, "00", 1, 0);
177
178 /* -7.70 */
179 test_numeric_param(hstmt, 1, "0203", 3, 2);
180
181 /* 0.12345, with 1-6 digit precision: */
182 test_numeric_param(hstmt, 1, "3930", 1, 5);
183 test_numeric_param(hstmt, 1, "3930", 2, 5);
184 test_numeric_param(hstmt, 1, "3930", 3, 5);
185 test_numeric_param(hstmt, 1, "3930", 4, 5);
186 test_numeric_param(hstmt, 1, "3930", 5, 5);
187 test_numeric_param(hstmt, 1, "3930", 6, 5);
188
189 /* large scale with small value */
190 test_numeric_param(hstmt, 1, "0203", 3, 50);
191
192 /* medium-sized scale and precision */
193 test_numeric_param(hstmt, 1, "0203", 25, 80);
194
195 /* max length output; negative with max scale and decimal dot */
196 test_numeric_param(hstmt, 0, "FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF", 40, 127);
197
198 /* large scale with small value */
199 test_numeric_param(hstmt, 1, "0203", 3, 50);
200
201
202 /**** Test fetching SQL_NUMERIC_STRUCT results ****/
203 printf("Testing SQL_NUMERIC_STRUCT results...\n\n");
204
205 test_numeric_result(hstmt, "25.212");
206 test_numeric_result(hstmt, "24197857161011715162171839636988778104");
207 test_numeric_result(hstmt, "12345678901234567890123456789012345678");
208 /* highest number */
209 test_numeric_result(hstmt, "340282366920938463463374607431768211455");
210 /* overflow */
211 test_numeric_result(hstmt, "340282366920938463463374607431768211456");
212 test_numeric_result(hstmt, "340282366920938463463374607431768211457");
213
214 test_numeric_result(hstmt, "-0");
215 test_numeric_result(hstmt, "0");
216 test_numeric_result(hstmt, "-7.70");
217 test_numeric_result(hstmt, "999999999999");
218
219 /* Clean up */
220 test_disconnect();
221
222 return 0;
223 }
224