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