1 #include "common.h"
2 #include <ctype.h>
3 
4 /*
5  * SQLSetStmtAttr
6  */
7 
8 static int g_result = 0;
9 static unsigned int line_num;
10 
11 #ifdef __GNUC__
12 static void fatal(const char *msg, ...) __attribute__((noreturn));
13 #endif
14 
15 static void
fatal(const char * msg,...)16 fatal(const char *msg, ...)
17 {
18 	va_list ap;
19 
20 	va_start(ap, msg);
21 	vfprintf(stderr, msg, ap);
22 	va_end(ap);
23 
24 	exit(1);
25 }
26 
27 static int
get_int(const char * s)28 get_int(const char *s)
29 {
30 	char *end;
31 	long l;
32 
33 	if (!s)
34 		fatal("Line %u: NULL int\n", line_num);
35 	l = strtol(s, &end, 0);
36 	if (end[0])
37 		fatal("Line %u: Invalid int\n", line_num);
38 	return (int) l;
39 }
40 
41 struct lookup_int
42 {
43 	const char *name;
44 	int value;
45 };
46 
47 static int
lookup(const char * name,const struct lookup_int * table)48 lookup(const char *name, const struct lookup_int *table)
49 {
50 	if (!table)
51 		return get_int(name);
52 
53 	for (; table->name; ++table)
54 		if (strcmp(table->name, name) == 0)
55 			return table->value;
56 
57 	return get_int(name);
58 }
59 
60 static const char *
unlookup(int value,const struct lookup_int * table)61 unlookup(int value, const struct lookup_int *table)
62 {
63 	if (!table)
64 		return "??";
65 
66 	for (; table->name; ++table)
67 		if (table->value == value)
68 			return table->name;
69 
70 	return "??";
71 }
72 
73 static struct lookup_int concurrency[] = {
74 #define TYPE(s) { #s, s }
75 	TYPE(SQL_CONCUR_READ_ONLY),
76 	TYPE(SQL_CONCUR_LOCK),
77 	TYPE(SQL_CONCUR_ROWVER),
78 	TYPE(SQL_CONCUR_VALUES),
79 #undef TYPE
80 	{ NULL, 0 }
81 };
82 
83 static struct lookup_int scrollable[] = {
84 #define TYPE(s) { #s, s }
85 	TYPE(SQL_NONSCROLLABLE),
86 	TYPE(SQL_SCROLLABLE),
87 #undef TYPE
88 	{ NULL, 0 }
89 };
90 
91 static struct lookup_int sensitivity[] = {
92 #define TYPE(s) { #s, s }
93 	TYPE(SQL_UNSPECIFIED),
94 	TYPE(SQL_INSENSITIVE),
95 	TYPE(SQL_SENSITIVE),
96 #undef TYPE
97 	{ NULL, 0 }
98 };
99 
100 static struct lookup_int cursor_type[] = {
101 #define TYPE(s) { #s, s }
102 	TYPE(SQL_CURSOR_FORWARD_ONLY),
103 	TYPE(SQL_CURSOR_STATIC),
104 	TYPE(SQL_CURSOR_KEYSET_DRIVEN),
105 	TYPE(SQL_CURSOR_DYNAMIC),
106 #undef TYPE
107 	{ NULL, 0 }
108 };
109 
110 static struct lookup_int noscan[] = {
111 #define TYPE(s) { #s, s }
112 	TYPE(SQL_NOSCAN_OFF),
113 	TYPE(SQL_NOSCAN_ON),
114 #undef TYPE
115 	{ NULL, 0 }
116 };
117 
118 static struct lookup_int retrieve_data[] = {
119 #define TYPE(s) { #s, s }
120 	TYPE(SQL_RD_ON),
121 	TYPE(SQL_RD_OFF),
122 #undef TYPE
123 	{ NULL, 0 }
124 };
125 
126 static struct lookup_int simulate_cursor[] = {
127 #define TYPE(s) { #s, s }
128 	TYPE(SQL_SC_NON_UNIQUE),
129 	TYPE(SQL_SC_TRY_UNIQUE),
130 	TYPE(SQL_SC_UNIQUE),
131 #undef TYPE
132 	{ NULL, 0 }
133 };
134 
135 static struct lookup_int use_bookmarks[] = {
136 #define TYPE(s) { #s, s }
137 	TYPE(SQL_UB_OFF),
138 	TYPE(SQL_UB_VARIABLE),
139 	TYPE(SQL_UB_FIXED),
140 #undef TYPE
141 	{ NULL, 0 }
142 };
143 
144 typedef enum
145 {
146 	type_INTEGER,
147 	type_UINTEGER,
148 	type_SMALLINT,
149 	type_LEN,
150 	type_CHARP,
151 	type_DESC,
152 	type_VOIDP
153 } test_type_t;
154 
155 struct attribute
156 {
157 	const char *name;
158 	int value;
159 	test_type_t type;
160 	const struct lookup_int *lookup;
161 };
162 
163 static const struct attribute attributes[] = {
164 #define ATTR(s,t) { #s, s, type_##t, NULL }
165 #define ATTR2(s,t,l) { #s, s, type_##t, l }
166 	ATTR(SQL_ATTR_APP_PARAM_DESC, DESC),
167 	ATTR(SQL_ATTR_APP_ROW_DESC, DESC),
168 	ATTR(SQL_ATTR_ASYNC_ENABLE, UINTEGER),
169 	ATTR2(SQL_ATTR_CONCURRENCY, UINTEGER, concurrency),
170 	ATTR2(SQL_ATTR_CURSOR_SCROLLABLE, UINTEGER, scrollable),
171 	ATTR2(SQL_ATTR_CURSOR_SENSITIVITY, UINTEGER, sensitivity),
172 	ATTR2(SQL_ATTR_CURSOR_TYPE, UINTEGER, cursor_type),
173 	ATTR(SQL_ATTR_ENABLE_AUTO_IPD, UINTEGER),
174 	ATTR(SQL_ATTR_FETCH_BOOKMARK_PTR, VOIDP),
175 	ATTR(SQL_ATTR_IMP_PARAM_DESC, DESC),
176 	ATTR(SQL_ATTR_IMP_ROW_DESC, DESC),
177 	ATTR(SQL_ATTR_KEYSET_SIZE, UINTEGER),
178 	ATTR(SQL_ATTR_MAX_LENGTH, UINTEGER),
179 	ATTR(SQL_ATTR_MAX_ROWS, UINTEGER),
180 	ATTR(SQL_ATTR_METADATA_ID, UINTEGER),
181 	ATTR2(SQL_ATTR_NOSCAN, UINTEGER, noscan),
182 	ATTR(SQL_ATTR_PARAM_BIND_OFFSET_PTR, VOIDP),
183 	ATTR(SQL_ATTR_PARAM_BIND_OFFSET_PTR, VOIDP),
184 	ATTR(SQL_ATTR_PARAM_BIND_TYPE, UINTEGER),
185 	ATTR(SQL_ATTR_PARAM_OPERATION_PTR, VOIDP),
186 	ATTR(SQL_ATTR_PARAM_STATUS_PTR, VOIDP),
187 	ATTR(SQL_ATTR_PARAMS_PROCESSED_PTR, VOIDP),
188 	ATTR(SQL_ATTR_PARAMSET_SIZE, UINTEGER),
189 	ATTR(SQL_ATTR_QUERY_TIMEOUT, UINTEGER),
190 	ATTR2(SQL_ATTR_RETRIEVE_DATA, UINTEGER, retrieve_data),
191 	ATTR(SQL_ATTR_ROW_ARRAY_SIZE, UINTEGER),
192 	ATTR(SQL_ATTR_ROW_BIND_OFFSET_PTR, VOIDP),
193 	ATTR(SQL_ATTR_ROW_BIND_TYPE, UINTEGER),
194 	ATTR(SQL_ATTR_ROW_NUMBER, UINTEGER),
195 	ATTR(SQL_ATTR_ROW_OPERATION_PTR, VOIDP),
196 	ATTR(SQL_ATTR_ROW_STATUS_PTR, VOIDP),
197 	ATTR(SQL_ATTR_ROWS_FETCHED_PTR, VOIDP),
198 	ATTR2(SQL_ATTR_SIMULATE_CURSOR, UINTEGER, simulate_cursor),
199 	ATTR2(SQL_ATTR_USE_BOOKMARKS, UINTEGER, use_bookmarks),
200 #undef ATTR2
201 #undef ATTR
202 };
203 
204 static const struct attribute *
lookup_attr(const char * name)205 lookup_attr(const char *name)
206 {
207 	unsigned int i;
208 
209 	if (!name)
210 		fatal("Line %u: NULL attribute\n", line_num);
211 	for (i = 0; i < TDS_VECTOR_SIZE(attributes); ++i)
212 		if (strcmp(attributes[i].name, name) == 0 || strcmp(attributes[i].name + 4, name) == 0)
213 			return &attributes[i];
214 	fatal("Line %u: attribute %s not found\n", line_num, name);
215 	return NULL;
216 }
217 
218 #define SEP " \t\n"
219 
220 #define ATTR_PARAMS const struct attribute *attr, int expected
221 typedef int (*get_attr_t) (ATTR_PARAMS);
222 
223 static int
get_attr_stmt(ATTR_PARAMS)224 get_attr_stmt(ATTR_PARAMS)
225 {
226 	SQLINTEGER i, ind;
227 	SQLSMALLINT si;
228 	SQLLEN li;
229 	SQLRETURN ret;
230 
231 	ret = SQL_ERROR;
232 	switch (attr->type) {
233 	case type_INTEGER:
234 	case type_UINTEGER:
235 		i = 0xdeadbeef;
236 		ret = SQLGetStmtAttr(odbc_stmt, attr->value, (SQLPOINTER) & i, sizeof(SQLINTEGER), &ind);
237 		break;
238 	case type_SMALLINT:
239 		si = 0xbeef;
240 		ret = SQLGetStmtAttr(odbc_stmt, attr->value, (SQLPOINTER) & si, sizeof(SQLSMALLINT), &ind);
241 		i = si;
242 		break;
243 	case type_LEN:
244 		li = 0xdeadbeef;
245 		ret = SQLGetStmtAttr(odbc_stmt, attr->value, (SQLPOINTER) & li, sizeof(SQLLEN), &ind);
246 		i = li;
247 		break;
248 	case type_VOIDP:
249 	case type_DESC:
250 	case type_CHARP:
251 		fatal("Line %u: CHAR* check still not supported\n", line_num);
252 		break;
253 	}
254 	if (!SQL_SUCCEEDED(ret))
255 		fatal("Line %u: failure not expected\n", line_num);
256 	return i;
257 }
258 
259 #if 0
260 /* do not retry any attribute just return expected value so to make caller happy */
261 static int
262 get_attr_none(ATTR_PARAMS)
263 {
264 	return expected;
265 }
266 #endif
267 
268 int
main(int argc,char * argv[])269 main(int argc, char *argv[])
270 {
271 #define TEST_FILE "attributes.in"
272 	const char *in_file = FREETDS_SRCDIR "/" TEST_FILE;
273 	FILE *f;
274 	char buf[256];
275 	SQLINTEGER i;
276 	SQLLEN len;
277 	get_attr_t get_attr_p = get_attr_stmt;
278 
279 	odbc_connect();
280 	/* TODO find another way */
281 	odbc_check_cursor();
282 	odbc_command("SET TEXTSIZE 4096");
283 
284 	SQLBindCol(odbc_stmt, 1, SQL_C_SLONG, &i, sizeof(i), &len);
285 
286 	f = fopen(in_file, "r");
287 	if (!f)
288 		f = fopen(TEST_FILE, "r");
289 	if (!f) {
290 		fprintf(stderr, "error opening test file\n");
291 		exit(1);
292 	}
293 
294 	line_num = 0;
295 	while (fgets(buf, sizeof(buf), f)) {
296 		char *p = buf, *cmd;
297 
298 		++line_num;
299 
300 		while (isspace((unsigned char) *p))
301 			++p;
302 		cmd = strtok(p, SEP);
303 
304 		/* skip comments */
305 		if (!cmd || cmd[0] == '#' || cmd[0] == 0 || cmd[0] == '\n')
306 			continue;
307 
308 		if (strcmp(cmd, "odbc") == 0) {
309 			int odbc3 = get_int(strtok(NULL, SEP)) == 3 ? 1 : 0;
310 
311 			if (odbc_use_version3 != odbc3) {
312 				odbc_use_version3 = odbc3;
313 				odbc_disconnect();
314 				odbc_connect();
315 				odbc_command("SET TEXTSIZE 4096");
316 				SQLBindCol(odbc_stmt, 1, SQL_C_SLONG, &i, sizeof(i), &len);
317 			}
318 			continue;
319 		}
320 
321 		/* set attribute */
322 		if (strcmp(cmd, "set") == 0) {
323 			const struct attribute *attr = lookup_attr(strtok(NULL, SEP));
324 			char *value = strtok(NULL, SEP);
325 			SQLRETURN ret;
326 
327 			if (!value)
328 				fatal("Line %u: value not defined\n", line_num);
329 
330 			ret = SQL_ERROR;
331 			switch (attr->type) {
332 			case type_UINTEGER:
333 			case type_INTEGER:
334 				ret = SQLSetStmtAttr(odbc_stmt, attr->value, TDS_INT2PTR(lookup(value, attr->lookup)),
335 						      sizeof(SQLINTEGER));
336 				break;
337 			case type_SMALLINT:
338 				ret = SQLSetStmtAttr(odbc_stmt, attr->value, TDS_INT2PTR(lookup(value, attr->lookup)),
339 						      sizeof(SQLSMALLINT));
340 				break;
341 			case type_LEN:
342 				ret = SQLSetStmtAttr(odbc_stmt, attr->value, TDS_INT2PTR(lookup(value, attr->lookup)),
343 						      sizeof(SQLLEN));
344 				break;
345 			case type_CHARP:
346 				ret = SQLSetStmtAttr(odbc_stmt, attr->value, (SQLPOINTER) value, SQL_NTS);
347 				break;
348 			case type_VOIDP:
349 			case type_DESC:
350 				fatal("Line %u: not implemented\n");
351 			}
352 			if (!SQL_SUCCEEDED(ret))
353 				fatal("Line %u: failure not expected setting statement attribute\n", line_num);
354 			get_attr_p = get_attr_stmt;
355 			continue;
356 		}
357 
358 		/* test attribute */
359 		if (strcmp(cmd, "attr") == 0) {
360 			const struct attribute *attr = lookup_attr(strtok(NULL, SEP));
361 			char *value = strtok(NULL, SEP);
362 			int i, expected = lookup(value, attr->lookup);
363 
364 			if (!value)
365 				fatal("Line %u: value not defined\n", line_num);
366 
367 			i = get_attr_p(attr, expected);
368 			if (i != expected) {
369 				g_result = 1;
370 				fprintf(stderr, "Line %u: invalid %s got %d(%s) expected %s\n", line_num, attr->name, i, unlookup(i, attr->lookup), value);
371 			}
372 			continue;
373 		}
374 
375 		if (strcmp(cmd, "reset") == 0) {
376 			odbc_reset_statement();
377 			continue;
378 		}
379 
380 		fatal("Line %u: command '%s' not handled\n", line_num, cmd);
381 	}
382 
383 	fclose(f);
384 	odbc_disconnect();
385 
386 	printf("Done.\n");
387 	return g_result;
388 }
389