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