1 #include "common.h"
2 
3 SQLHENV env;
4 SQLHDBC conn;
5 
6 void
print_diag(char * msg,SQLSMALLINT htype,SQLHANDLE handle)7 print_diag(char *msg, SQLSMALLINT htype, SQLHANDLE handle)
8 {
9 	char		sqlstate[32];
10 	char		message[1000];
11 	SQLINTEGER	nativeerror;
12 	SQLSMALLINT textlen;
13 	SQLRETURN	ret;
14 	SQLSMALLINT	recno = 0;
15 
16 	if (msg)
17 		printf("%s\n", msg);
18 
19 	do
20 	{
21 		recno++;
22 		ret = SQLGetDiagRec(htype, handle, recno, sqlstate, &nativeerror,
23 							message, sizeof(message), &textlen);
24 		if (ret == SQL_INVALID_HANDLE)
25 			printf("Invalid handle\n");
26 		else if (SQL_SUCCEEDED(ret))
27 			printf("%s=%s\n", sqlstate, message);
28 	} while (ret == SQL_SUCCESS);
29 
30 	if (ret == SQL_NO_DATA && recno == 1)
31 		printf("No error information\n");
32 }
33 
34 const char * const default_dsn = "psqlodbc_test_dsn";
35 const char * const test_dsn_env = "PSQLODBC_TEST_DSN";
36 const char * const test_dsn_ansi = "psqlodbc_test_dsn_ansi";
37 
get_test_dsn(void)38 const char *get_test_dsn(void)
39 {
40 	char	*env = getenv(test_dsn_env);
41 
42 	if (NULL != env && env[0])
43 		return env;
44 	return default_dsn;
45 }
46 
IsAnsi(void)47 int IsAnsi(void)
48 {
49 	return (NULL != strstr(get_test_dsn(), "_ansi"));
50 }
51 
52 void
test_connect_ext(char * extraparams)53 test_connect_ext(char *extraparams)
54 {
55 	SQLRETURN ret;
56 	SQLCHAR str[1024];
57 	SQLSMALLINT strl;
58 	SQLCHAR dsn[1024];
59 	const char * const test_dsn = get_test_dsn();
60 	char *envvar;
61 
62 	/*
63 	 *	Use an environment variable to switch settings of connection
64 	 *	strings throughout the regression test. Note that extraparams
65 	 *	parameters have precedence over the environment variable.
66 	 *	ODBC spec says
67 	 *		If any keywords are repeated in the connection string,
68 	 *		the driver uses the value associated with the first
69 	 *		occurrence of the keyword.
70 	 *	But the current psqlodbc driver uses the value associated with
71 	 *	the last occurrence of the keyword. Here we place extraparams
72 	 *	both before and after the value of the environment variable
73 	 *	so as to protect the priority order whichever way we take.
74 	 */
75 	if ((envvar = getenv("COMMON_CONNECTION_STRING_FOR_REGRESSION_TEST")) != NULL && envvar[0] != '\0')
76 	{
77 		if (NULL == extraparams)
78 			snprintf(dsn, sizeof(dsn), "DSN=%s;%s", test_dsn, envvar);
79 		else
80 			snprintf(dsn, sizeof(dsn), "DSN=%s;%s;%s;%s",
81 			 test_dsn, extraparams, envvar, extraparams);
82 	}
83 	else
84 		snprintf(dsn, sizeof(dsn), "DSN=%s;%s",
85 			 test_dsn, extraparams ? extraparams : "");
86 
87 	SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
88 
89 	SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
90 
91 	SQLAllocHandle(SQL_HANDLE_DBC, env, &conn);
92 	ret = SQLDriverConnect(conn, NULL, dsn, SQL_NTS,
93 						   str, sizeof(str), &strl,
94 						   SQL_DRIVER_COMPLETE);
95 	if (SQL_SUCCEEDED(ret)) {
96 		printf("connected\n");
97 	} else {
98 		print_diag("SQLDriverConnect failed.", SQL_HANDLE_DBC, conn);
99 		fflush(stdout);
100 		exit(1);
101 	}
102 }
103 
104 void
test_connect(void)105 test_connect(void)
106 {
107 	test_connect_ext(NULL);
108 }
109 
110 void
test_disconnect(void)111 test_disconnect(void)
112 {
113 	SQLRETURN rc;
114 
115 	printf("disconnecting\n");
116 	rc = SQLDisconnect(conn);
117 	if (!SQL_SUCCEEDED(rc))
118 	{
119 		print_diag("SQLDisconnect failed", SQL_HANDLE_DBC, conn);
120 		fflush(stdout);
121 		exit(1);
122 	}
123 
124 	rc = SQLFreeHandle(SQL_HANDLE_DBC, conn);
125 	if (!SQL_SUCCEEDED(rc))
126 	{
127 		print_diag("SQLFreeHandle failed", SQL_HANDLE_DBC, conn);
128 		fflush(stdout);
129 		exit(1);
130 	}
131 	conn = NULL;
132 
133 	rc = SQLFreeHandle(SQL_HANDLE_ENV, env);
134 	if (!SQL_SUCCEEDED(rc))
135 	{
136 		print_diag("SQLFreeHandle failed", SQL_HANDLE_ENV, env);
137 		fflush(stdout);
138 		exit(1);
139 	}
140 	env = NULL;
141 }
142 
143 const char *
datatype_str(SQLSMALLINT datatype)144 datatype_str(SQLSMALLINT datatype)
145 {
146 	static char buf[100];
147 
148 	switch (datatype)
149 	{
150 		case SQL_CHAR:
151 			return "CHAR";
152 		case SQL_VARCHAR:
153 			return "VARCHAR";
154 		case SQL_LONGVARCHAR:
155 			return "LONGVARCHAR";
156 		case SQL_WCHAR:
157 			return "WCHAR";
158 		case SQL_WVARCHAR:
159 			return "WVARCHAR";
160 		case SQL_WLONGVARCHAR:
161 			return "WLONGVARCHAR";
162 		case SQL_DECIMAL:
163 			return "DECIMAL";
164 		case SQL_NUMERIC:
165 			return "NUMERIC";
166 		case SQL_SMALLINT:
167 			return "SMALLINT";
168 		case SQL_INTEGER:
169 			return "INTEGER";
170 		case SQL_REAL:
171 			return "REAL";
172 		case SQL_FLOAT:
173 			return "FLOAT";
174 		case SQL_DOUBLE:
175 			return "DOUBLE";
176 		case SQL_BIT:
177 			return "BIT";
178 		case SQL_TINYINT:
179 			return "TINYINT";
180 		case SQL_BIGINT:
181 			return "BIGINT";
182 		case SQL_BINARY:
183 			return "BINARY";
184 		case SQL_VARBINARY:
185 			return "VARBINARY";
186 		case SQL_LONGVARBINARY:
187 			return "LONGVARBINARY";
188 		case SQL_TYPE_DATE:
189 			return "TYPE_DATE";
190 		case SQL_TYPE_TIME:
191 			return "TYPE_TIME";
192 		case SQL_TYPE_TIMESTAMP:
193 			return "TYPE_TIMESTAMP";
194 		case SQL_GUID:
195 			return "GUID";
196 		default:
197 			snprintf(buf, sizeof(buf), "unknown sql type %d", datatype);
198 			return buf;
199 	}
200 }
201 
nullable_str(SQLSMALLINT nullable)202 const char *nullable_str(SQLSMALLINT nullable)
203 {
204 	static char buf[100];
205 
206 	switch(nullable)
207 	{
208 		case SQL_NO_NULLS:
209 			return "not nullable";
210 		case SQL_NULLABLE:
211 			return "nullable";
212 		case SQL_NULLABLE_UNKNOWN:
213 			return "nullable_unknown";
214 		default:
215 			snprintf(buf, sizeof(buf), "unknown nullable value %d", nullable);
216 			return buf;
217 	}
218 }
219 
220 void
print_result_meta_series(HSTMT hstmt,SQLSMALLINT * colids,SQLSMALLINT numcols)221 print_result_meta_series(HSTMT hstmt,
222 						 SQLSMALLINT *colids,
223 						 SQLSMALLINT numcols)
224 {
225 	int i;
226 
227 	printf("Result set metadata:\n");
228 
229 	for (i = 0; i < numcols; i++)
230 	{
231 		SQLRETURN rc;
232 		SQLCHAR colname[50];
233 		SQLSMALLINT colnamelen;
234 		SQLSMALLINT datatype;
235 		SQLULEN colsize;
236 		SQLSMALLINT decdigits;
237 		SQLSMALLINT nullable;
238 
239 		rc = SQLDescribeCol(hstmt, colids[i],
240 							colname, sizeof(colname),
241 							&colnamelen,
242 							&datatype,
243 							&colsize,
244 							&decdigits,
245 							&nullable);
246 		if (!SQL_SUCCEEDED(rc))
247 		{
248 			print_diag("SQLDescribeCol failed", SQL_HANDLE_STMT, hstmt);
249 			return;
250 		}
251 		printf("%s: %s(%u) digits: %d, %s\n",
252 			   colname, datatype_str(datatype), (unsigned int) colsize,
253 			   decdigits, nullable_str(nullable));
254 	}
255 }
256 
257 void
print_result_meta(HSTMT hstmt)258 print_result_meta(HSTMT hstmt)
259 {
260 	SQLRETURN rc;
261 	SQLSMALLINT numcols, i;
262 	SQLSMALLINT *colids;
263 
264 	rc = SQLNumResultCols(hstmt, &numcols);
265 	if (!SQL_SUCCEEDED(rc))
266 	{
267 		print_diag("SQLNumResultCols failed", SQL_HANDLE_STMT, hstmt);
268 		return;
269 	}
270 
271 	colids = (SQLSMALLINT *) malloc(numcols * sizeof(SQLSMALLINT));
272 	for (i = 0; i < numcols; i++)
273 		colids[i] = i + 1;
274 	print_result_meta_series(hstmt, colids, numcols);
275 	free(colids);
276 }
277 
278 /*
279  * Initialize a buffer with "XxXxXx..." to indicate an uninitialized value.
280  */
281 static void
invalidate_buf(char * buf,size_t len)282 invalidate_buf(char *buf, size_t len)
283 {
284 	size_t i;
285 
286 	for (i = 0; i < len; i++)
287 	{
288 		if (i % 2 == 0)
289 			buf[i] = 'X';
290 		else
291 			buf[i] = 'x';
292 	}
293 	buf[len - 1] = '\0';
294 }
295 
296 /*
297  * Print result only for the selected columns.
298  */
299 void
print_result_series(HSTMT hstmt,SQLSMALLINT * colids,SQLSMALLINT numcols,SQLINTEGER rowcount)300 print_result_series(HSTMT hstmt, SQLSMALLINT *colids, SQLSMALLINT numcols, SQLINTEGER rowcount)
301 {
302 	SQLRETURN rc;
303 	SQLINTEGER	rowc = 0;
304 
305 	printf("Result set:\n");
306 	while (rowcount <0 || rowc < rowcount)
307 	{
308 		rc = SQLFetch(hstmt);
309 		if (rc == SQL_NO_DATA)
310 			break;
311 		if (rc == SQL_SUCCESS)
312 		{
313 			char buf[40];
314 			int i;
315 			SQLLEN ind;
316 
317 			rowc++;
318 			for (i = 0; i < numcols; i++)
319 			{
320 				/*
321 				 * Initialize the buffer with garbage, so that we see readily
322 				 * if SQLGetData fails to set the value properly or forgets
323 				 * to null-terminate it.
324 				 */
325 				invalidate_buf(buf, sizeof(buf));
326 				rc = SQLGetData(hstmt, colids[i], SQL_C_CHAR, buf, sizeof(buf), &ind);
327 				if (!SQL_SUCCEEDED(rc))
328 				{
329 					print_diag("SQLGetData failed", SQL_HANDLE_STMT, hstmt);
330 					return;
331 				}
332 				if (ind == SQL_NULL_DATA)
333 					strcpy(buf, "NULL");
334 				printf("%s%s", (i > 0) ? "\t" : "", buf);
335 			}
336 			printf("\n");
337 		}
338 		else
339 		{
340 			print_diag("SQLFetch failed", SQL_HANDLE_STMT, hstmt);
341 			fflush(stdout);
342 			exit(1);
343 		}
344 	}
345 }
346 
347 /*
348  * Print result on all the columns
349  */
350 void
print_result(HSTMT hstmt)351 print_result(HSTMT hstmt)
352 {
353 	SQLRETURN rc;
354 	SQLSMALLINT numcols, i;
355 	SQLSMALLINT *colids;
356 
357 	rc = SQLNumResultCols(hstmt, &numcols);
358 	if (!SQL_SUCCEEDED(rc))
359 	{
360 		print_diag("SQLNumResultCols failed", SQL_HANDLE_STMT, hstmt);
361 		return;
362 	}
363 
364 	colids = (SQLSMALLINT *) malloc(numcols * sizeof(SQLSMALLINT));
365 	for (i = 0; i < numcols; i++)
366 		colids[i] = i + 1;
367 	print_result_series(hstmt, colids, numcols, -1);
368 	free(colids);
369 }
370