Line data Source code
1 : #include "common.h"
2 :
3 : #include <stdarg.h>
4 : #include <assert.h>
5 : #include <ctype.h>
6 :
7 : #ifdef __VMS
8 : #include <unixlib.h>
9 : #endif
10 :
11 : #if HAVE_SYS_SOCKET_H
12 : #include <sys/socket.h>
13 : #endif /* HAVE_SYS_SOCKET_H */
14 :
15 : #if HAVE_SYS_STAT_H
16 : #include <sys/stat.h>
17 : #endif /* HAVE_SYS_STAT_H */
18 :
19 : #if HAVE_NETINET_IN_H
20 : #include <netinet/in.h>
21 : #endif /* HAVE_NETINET_IN_H */
22 :
23 : #if defined(UNIXODBC) || defined(_WIN32)
24 : #include <odbcinst.h>
25 : #endif
26 :
27 : #include <odbcss.h>
28 : #include <freetds/sysdep_private.h>
29 : #include <freetds/replacements.h>
30 :
31 : struct odbc_buf{
32 : struct odbc_buf *next;
33 : void *buf;
34 : };
35 :
36 : HENV odbc_env;
37 : HDBC odbc_conn;
38 : HSTMT odbc_stmt;
39 : bool odbc_use_version3 = false;
40 : void (*odbc_set_conn_attr)(void) = NULL;
41 : const char *odbc_conn_additional_params = NULL;
42 :
43 : static int freetds_driver = -1;
44 : static int tds_version = -1;
45 : static char db_str_version[32];
46 :
47 : static bool
48 1136 : check_lib(char *path, const char *file)
49 : {
50 1136 : size_t len = strlen(path);
51 : FILE *f;
52 :
53 1136 : strcat(path, file);
54 1136 : f = fopen(path, "rb");
55 1136 : if (f) {
56 1136 : fclose(f);
57 1136 : return true;
58 : }
59 0 : path[len] = 0;
60 0 : return false;
61 : }
62 :
63 : /* this should be extended with all possible systems... */
64 : static const char *const search_driver[] = {
65 : ".libs/libtdsodbc.so",
66 : ".libs/libtdsodbc.sl",
67 : ".libs/libtdsodbc.dylib",
68 : ".libs/libtdsodbc.dll",
69 : "_libs/libtdsodbc.dll",
70 : "_libs/libtdsodbc.exe", /* VMS */
71 : "debug/tdsodbc.dll",
72 : "release/tdsodbc.dll",
73 : "libtdsodbc.so",
74 : "tdsodbc.dll",
75 : NULL
76 : };
77 :
78 : int
79 1136 : odbc_read_login_info(void)
80 : {
81 1136 : const char *PWD = DEFAULT_PWD_PATH;
82 : #if !defined(_WIN32) || defined(TDS_NO_DM)
83 1136 : FILE *in = NULL;
84 : #endif
85 : char *s1;
86 : const char *const *search_p;
87 : char path[1024];
88 : size_t len;
89 1136 : bool ini_override = true;
90 : #if defined(_WIN32) && !defined(TDS_NO_DM)
91 : UWORD old_config_mode;
92 : #endif
93 :
94 1136 : setbuf(stdout, NULL);
95 1136 : setbuf(stderr, NULL);
96 :
97 1136 : PWD = try_read_login_info_base(&common_pwd, PWD);
98 1136 : if (!PWD && !read_login_info_base(&common_pwd, "PWD"))
99 : return 1;
100 :
101 : /*
102 : * Some of the ODBC tests "go in the front door", i.e. call
103 : * SQLDriverConnect() which will look up an entry in ODBCINST.INI .
104 : * In "connect" and "timeout3" they look up the entry FreeTDS.
105 : * However your system ODBCINST.INI probably doesn't point to
106 : * the driver we just built and are trying to test.
107 : *
108 : * So here we are finding the ODBC driver we just built and saving it
109 : * into a custom field common_pwd.driver, and those tests have code to
110 : * build a dummy ODBCINST.INI with an entry called FreeTDS and using
111 : * common_pwd.driver sa the driver.
112 : */
113 : #ifndef _WIN32
114 1136 : if (!getcwd(path, sizeof(path)))
115 : #else
116 : if (!_getcwd(path, sizeof(path)))
117 : #endif
118 : return 0;
119 : #ifdef __VMS
120 : {
121 : /* A hard-coded driver path has to be in unix syntax to be recognized as such. */
122 : const char *unixspec = decc$translate_vms(path);
123 :
124 : if ((int) unixspec != 0 && (int) unixspec != -1)
125 : strcpy(path, unixspec);
126 : }
127 : #endif
128 1136 : len = strlen(path);
129 1136 : if (len < 10 || (strcasecmp(path + len - 10, "/unittests") != 0
130 : #ifdef _WIN32
131 : && strcasecmp(path + len - 10, "\\unittests") != 0
132 : #endif
133 : ))
134 : return 0;
135 1136 : path[len - 9] = 0;
136 1136 : for (search_p = search_driver; *search_p; ++search_p) {
137 1136 : if (check_lib(path, *search_p))
138 : break;
139 : }
140 1136 : if (!*search_p)
141 : return 0;
142 1136 : strcpy(common_pwd.driver, path);
143 :
144 1136 : s1 = getenv("TDSINIOVERRIDE");
145 1136 : if (s1 && atoi(s1) == 0)
146 0 : ini_override = false;
147 :
148 : #if !defined(_WIN32) || defined(TDS_NO_DM)
149 : /* craft out odbc.ini, avoid to read wrong one */
150 1136 : sprintf(path, "odbc.ini.%d", (int) getpid());
151 1136 : in = fopen(path, "w");
152 1136 : if (in) {
153 1136 : fprintf(in,
154 : "[%s]\nDriver = %s\nDatabase = %s\nServername = %s\n",
155 : common_pwd.server, common_pwd.driver, common_pwd.database, common_pwd.server);
156 1136 : fclose(in);
157 1136 : if (ini_override) {
158 1136 : setenv("ODBCINI", "./odbc.ini", 1);
159 1136 : setenv("SYSODBCINI", "./odbc.ini", 1);
160 1136 : rename(path, "odbc.ini");
161 : }
162 1136 : unlink(path);
163 : }
164 : #else
165 : if (ini_override && SQLGetConfigMode(&old_config_mode)) {
166 : ODBC_BUF *odbc_buf = NULL;
167 : LPCTSTR server = (LPCTSTR) T(common_pwd.server);
168 : LPCTSTR filename = (LPCTSTR) T("odbc.ini");
169 : SQLSetConfigMode(ODBC_USER_DSN);
170 : SQLWritePrivateProfileString(server, (LPCTSTR) T("Driver"), (void *) T(common_pwd.driver), filename);
171 : SQLWritePrivateProfileString(server, (LPCTSTR) T("Database"), (LPCTSTR) T(common_pwd.database), filename);
172 : SQLWritePrivateProfileString(server, (LPCTSTR) T("Servername"), (LPCTSTR) T(common_pwd.server), filename);
173 : SQLSetConfigMode(old_config_mode);
174 : ODBC_FREE();
175 : }
176 : #endif
177 : return 0;
178 : }
179 :
180 : void
181 0 : odbc_report_error(const char *errmsg, int line, const char *file)
182 : {
183 : SQLSMALLINT handletype;
184 : SQLHANDLE handle;
185 : SQLRETURN ret;
186 : SQLTCHAR sqlstate[6];
187 : SQLTCHAR msg[256];
188 0 : ODBC_BUF *odbc_buf = NULL;
189 :
190 0 : if (odbc_stmt) {
191 : handletype = SQL_HANDLE_STMT;
192 : handle = odbc_stmt;
193 0 : } else if (odbc_conn) {
194 : handletype = SQL_HANDLE_DBC;
195 : handle = odbc_conn;
196 : } else {
197 0 : handletype = SQL_HANDLE_ENV;
198 0 : handle = odbc_env;
199 : }
200 0 : if (errmsg[0]) {
201 0 : if (line)
202 0 : fprintf(stderr, "%s:%d %s\n", file, line, errmsg);
203 : else
204 0 : fprintf(stderr, "%s\n", errmsg);
205 : }
206 0 : ret = SQLGetDiagRec(handletype, handle, 1, sqlstate, NULL, msg, TDS_VECTOR_SIZE(msg), NULL);
207 0 : if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
208 0 : fprintf(stderr, "SQL error %s -- %s\n", C(sqlstate), C(msg));
209 0 : odbc_disconnect();
210 0 : ODBC_FREE();
211 0 : exit(1);
212 : }
213 :
214 : static void
215 0 : ReportODBCError(const char *errmsg, SQLSMALLINT handletype, SQLHANDLE handle, SQLRETURN rc, int line, const char *file)
216 : {
217 : SQLRETURN ret;
218 : SQLTCHAR sqlstate[6];
219 : SQLTCHAR msg[256];
220 0 : ODBC_BUF *odbc_buf = NULL;
221 :
222 0 : if (errmsg[0]) {
223 0 : if (line)
224 0 : fprintf(stderr, "%s:%d rc=%d %s\n", file, line, (int) rc, errmsg);
225 : else
226 0 : fprintf(stderr, "rc=%d %s\n", (int) rc, errmsg);
227 : }
228 0 : ret = SQLGetDiagRec(handletype, handle, 1, sqlstate, NULL, msg, TDS_VECTOR_SIZE(msg), NULL);
229 0 : if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
230 0 : fprintf(stderr, "SQL error %s -- %s\n", C(sqlstate), C(msg));
231 0 : odbc_disconnect();
232 0 : ODBC_FREE();
233 0 : exit(1);
234 : }
235 :
236 : int
237 1086 : odbc_connect(void)
238 : {
239 1086 : ODBC_BUF *odbc_buf = NULL;
240 : char command[512+10];
241 : const char *p;
242 :
243 1086 : if (odbc_read_login_info())
244 0 : exit(1);
245 :
246 1086 : if (odbc_use_version3) {
247 586 : CHKAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &odbc_env, "S");
248 586 : SQLSetEnvAttr(odbc_env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) (SQL_OV_ODBC3), SQL_IS_UINTEGER);
249 586 : CHKAllocHandle(SQL_HANDLE_DBC, odbc_env, &odbc_conn, "S");
250 : } else {
251 500 : CHKAllocEnv(&odbc_env, "S");
252 500 : CHKAllocConnect(&odbc_conn, "S");
253 : }
254 :
255 1086 : printf("odbctest\n--------\n\n");
256 1086 : printf("connection parameters:\nserver: '%s'\nuser: '%s'\npassword: '%s'\ndatabase: '%s'\n",
257 : common_pwd.server, common_pwd.user, "????" /* common_pwd.password */ , common_pwd.database);
258 :
259 1086 : p = getenv("ODBC_MARS");
260 1629 : if (p && atoi(p) != 0)
261 543 : SQLSetConnectAttr(odbc_conn, 1224 /*SQL_COPT_SS_MARS_ENABLED*/, (SQLPOINTER) 1 /*SQL_MARS_ENABLED_YES*/, SQL_IS_UINTEGER);
262 1086 : if (odbc_set_conn_attr)
263 64 : (*odbc_set_conn_attr)();
264 :
265 1086 : if (!odbc_conn_additional_params) {
266 996 : CHKConnect(T(common_pwd.server), SQL_NTS, T(common_pwd.user), SQL_NTS, T(common_pwd.password), SQL_NTS, "SI");
267 : } else {
268 : char *params;
269 : SQLSMALLINT len;
270 :
271 90 : assert(asprintf(¶ms, "DSN=%s;UID=%s;PWD=%s;DATABASE=%s;%s",
272 : common_pwd.server, common_pwd.user,
273 : common_pwd.password, common_pwd.database, odbc_conn_additional_params)
274 : >= 0);
275 90 : assert(params);
276 90 : CHKDriverConnect(NULL, T(params), SQL_NTS, (SQLTCHAR *) command, sizeof(command)/sizeof(SQLTCHAR),
277 : &len, SQL_DRIVER_NOPROMPT, "SI");
278 90 : free(params);
279 : }
280 :
281 1086 : CHKAllocStmt(&odbc_stmt, "S");
282 :
283 1086 : sprintf(command, "use %s", common_pwd.database);
284 1086 : printf("%s\n", command);
285 :
286 1086 : CHKExecDirect(T(command), SQL_NTS, "SI");
287 :
288 : #ifndef TDS_NO_DM
289 : /* unixODBC seems to require it */
290 : SQLMoreResults(odbc_stmt);
291 : #endif
292 1086 : ODBC_FREE();
293 1086 : return 0;
294 : }
295 :
296 : int
297 1206 : odbc_disconnect(void)
298 : {
299 1206 : if (odbc_stmt) {
300 1088 : SQLFreeStmt(odbc_stmt, SQL_DROP);
301 1088 : odbc_stmt = SQL_NULL_HSTMT;
302 : }
303 :
304 1206 : if (odbc_conn) {
305 1206 : SQLDisconnect(odbc_conn);
306 1206 : SQLFreeConnect(odbc_conn);
307 1206 : odbc_conn = SQL_NULL_HDBC;
308 : }
309 :
310 1206 : ODBC_FREE();
311 1206 : if (odbc_env) {
312 1206 : SQLFreeEnv(odbc_env);
313 1206 : odbc_env = SQL_NULL_HENV;
314 : }
315 1206 : ODBC_FREE();
316 :
317 1206 : freetds_driver = -1;
318 1206 : tds_version = -1;
319 1206 : db_str_version[0] = 0;
320 1206 : return 0;
321 : }
322 :
323 : SQLRETURN
324 1050 : odbc_command_with_result(HSTMT stmt, const char *command)
325 : {
326 : SQLRETURN ret;
327 1050 : ODBC_BUF *odbc_buf = NULL;
328 :
329 1050 : printf("%s\n", command);
330 1050 : ret = SQLExecDirect(stmt, T(command), SQL_NTS);
331 1050 : ODBC_FREE();
332 1050 : return ret;
333 : }
334 :
335 : static int ms_db = -1;
336 : bool
337 2940 : odbc_db_is_microsoft(void)
338 : {
339 2940 : ODBC_BUF *odbc_buf = NULL;
340 : SQLTCHAR buf[64];
341 : SQLSMALLINT len;
342 : int i;
343 :
344 2940 : if (ms_db < 0) {
345 268 : buf[0] = 0;
346 268 : CHKGetInfo(SQL_DBMS_NAME, buf, sizeof(buf), &len, "S");
347 5128 : for (i = 0; buf[i]; ++i)
348 7200 : buf[i] = tolower(buf[i]);
349 268 : ms_db = (strstr(C(buf), "microsoft") != NULL);
350 : }
351 2940 : ODBC_FREE();
352 2940 : return !!ms_db;
353 : }
354 :
355 : bool
356 1658 : odbc_driver_is_freetds(void)
357 : {
358 1658 : ODBC_BUF *odbc_buf = NULL;
359 : SQLTCHAR buf[64];
360 : SQLSMALLINT len;
361 : int i;
362 :
363 1658 : if (freetds_driver < 0) {
364 346 : buf[0] = 0;
365 346 : CHKGetInfo(SQL_DRIVER_NAME, buf, sizeof(buf), &len, "S");
366 4844 : for (i = 0; buf[i]; ++i)
367 6682 : buf[i] = tolower(buf[i]);
368 346 : freetds_driver = (strstr(C(buf), "tds") != NULL);
369 : }
370 1658 : ODBC_FREE();
371 1658 : return !!freetds_driver;
372 : }
373 :
374 : /* Detect protocol version using queries
375 : * This to make possible protocol discovery on drivers like MS
376 : */
377 : static int
378 0 : odbc_tds_version_long(void)
379 : {
380 : SQLRETURN ret;
381 : SQLSMALLINT scale, nullable, type;
382 : SQLULEN prec;
383 0 : ODBC_BUF *odbc_buf = NULL;
384 :
385 : /* statement must be in a consistent state to do the check */
386 0 : CHKExecDirect(T("select 1"), SQL_NTS, "S");
387 0 : odbc_reset_statement();
388 :
389 : /* select cast(123 as sql_variant) -> nvarchar('123') is 7.0 failure query is 5.0 ?? */
390 0 : ret = CHKExecDirect(T("select cast('123' as sql_variant)"), SQL_NTS, "SNoE");
391 0 : odbc_reset_statement();
392 0 : if (ret == SQL_ERROR) {
393 0 : ODBC_FREE();
394 0 : return 0x500;
395 : }
396 :
397 : /* see how bigint is returned, numeric means 7.0 */
398 0 : CHKExecDirect(T("select cast('123' as bigint)"), SQL_NTS, "S");
399 0 : CHKDescribeCol(1, NULL, 0, NULL, &type, &prec, &scale, &nullable, "S");
400 0 : odbc_reset_statement();
401 0 : if (type == SQL_NUMERIC || type == SQL_DECIMAL) {
402 0 : ODBC_FREE();
403 0 : return 0x700;
404 : }
405 0 : if (type != SQL_BIGINT) {
406 0 : fprintf(stderr, "Strange type returned trying to detect protocol version\n");
407 0 : odbc_disconnect();
408 0 : ODBC_FREE();
409 0 : exit(1);
410 : }
411 :
412 : /* select cast('123' as varchar(max)) -> ??? SQL_VARCHAR is 7.2 ?? */
413 0 : ret = CHKExecDirect(T("select cast('123' as varchar(max))"), SQL_NTS, "SE");
414 0 : if (ret == SQL_ERROR) {
415 0 : odbc_reset_statement();
416 0 : ODBC_FREE();
417 0 : return 0x701;
418 : }
419 0 : CHKDescribeCol(1, NULL, 0, NULL, &type, &prec, &scale, &nullable, "S");
420 0 : odbc_reset_statement();
421 0 : if (type == SQL_LONGVARCHAR) {
422 0 : ODBC_FREE();
423 0 : return 0x701;
424 : }
425 0 : if (type != SQL_VARCHAR) {
426 0 : fprintf(stderr, "Strange type returned trying to detect protocol version\n");
427 0 : odbc_disconnect();
428 0 : ODBC_FREE();
429 0 : exit(1);
430 : }
431 :
432 : /* select cast('12:13:14.1234' as time(4)) -> NVARCHAR('12:13:14.1234') is 7.2 else 7.3 */
433 0 : ret = CHKExecDirect(T("select cast('12:13:14.1234' as time(4))"), SQL_NTS, "SE");
434 0 : if (ret == SQL_ERROR) {
435 0 : odbc_reset_statement();
436 0 : ODBC_FREE();
437 0 : return 0x702;
438 : }
439 0 : CHKDescribeCol(1, NULL, 0, NULL, &type, &prec, &scale, &nullable, "S");
440 0 : odbc_reset_statement();
441 0 : if (scale == 0 && type == SQL_WVARCHAR) {
442 0 : ODBC_FREE();
443 0 : return 0x702;
444 : }
445 0 : if (scale != 4) {
446 0 : fprintf(stderr, "Strange scale returned trying to detect protocol version\n");
447 0 : odbc_disconnect();
448 0 : ODBC_FREE();
449 0 : exit(1);
450 : }
451 :
452 : /* select convert(varchar(10), 'hi' collate Latin1_General_100_CI_AI_SC_UTF8) ->
453 : * NVARCHAR('hi') is 7.3 else 7.4, at least from MSSQL 2019 */
454 0 : ret = CHKExecDirect(T("select convert(varchar(10), 'hi' collate Latin1_General_100_CI_AI_SC_UTF8)"), SQL_NTS, "SE");
455 0 : if (ret == SQL_ERROR) {
456 0 : odbc_reset_statement();
457 0 : ODBC_FREE();
458 0 : return 0x703;
459 : }
460 0 : CHKDescribeCol(1, NULL, 0, NULL, &type, &prec, &scale, &nullable, "S");
461 0 : odbc_reset_statement();
462 0 : if (type == SQL_WVARCHAR) {
463 0 : ODBC_FREE();
464 0 : return 0x703;
465 : }
466 0 : if (type != SQL_VARCHAR) {
467 0 : fprintf(stderr, "Strange type returned trying to detect protocol version\n");
468 0 : odbc_disconnect();
469 0 : ODBC_FREE();
470 0 : exit(1);
471 : }
472 :
473 0 : ODBC_FREE();
474 0 : return 0x704;
475 : }
476 :
477 : int
478 268 : odbc_tds_version(void)
479 : {
480 268 : ODBC_BUF *odbc_buf = NULL;
481 : SQLUINTEGER version;
482 : SQLSMALLINT len;
483 :
484 268 : if (odbc_driver_is_freetds() && tds_version < 0) {
485 124 : version = 0;
486 124 : len = 0;
487 124 : CHKGetInfo(1300 /* SQL_INFO_FREETDS_TDS_VERSION */, &version, sizeof(version), &len, "S");
488 124 : if (len == sizeof(version))
489 124 : tds_version = (version >> 16) << 8 | (version & 0xff);
490 : }
491 268 : if (tds_version < 0) {
492 0 : tds_version = odbc_tds_version_long();
493 : }
494 268 : ODBC_FREE();
495 268 : return tds_version;
496 : }
497 :
498 : const char *
499 358 : odbc_db_version(void)
500 : {
501 358 : if (!db_str_version[0]) {
502 98 : ODBC_BUF *odbc_buf = NULL;
503 : SQLTCHAR buf[32];
504 : SQLSMALLINT version_len;
505 :
506 98 : CHKGetInfo(SQL_DBMS_VER, buf, sizeof(buf), &version_len, "S");
507 98 : strcpy(db_str_version, C(buf));
508 98 : ODBC_FREE();
509 : }
510 :
511 358 : return db_str_version;
512 : }
513 :
514 : unsigned int
515 348 : odbc_db_version_int(void)
516 : {
517 : unsigned int h, l;
518 348 : if (sscanf(odbc_db_version(), "%u.%u.", &h, &l) != 2) {
519 0 : fprintf(stderr, "Wrong db version: %s\n", odbc_db_version());
520 0 : odbc_disconnect();
521 0 : exit(1);
522 : }
523 :
524 348 : return (h << 24) | ((l & 0xFFu) << 16);
525 : }
526 :
527 : void
528 268 : odbc_check_cols(int n, int line, const char * file)
529 : {
530 : SQLSMALLINT cols;
531 :
532 268 : if (n < 0) {
533 0 : CHKNumResultCols(&cols, "E");
534 0 : return;
535 : }
536 268 : CHKNumResultCols(&cols, "S");
537 268 : if (cols != n) {
538 0 : fprintf(stderr, "%s:%d: Expected %d columns returned %d\n", file, line, n, (int) cols);
539 0 : odbc_disconnect();
540 0 : exit(1);
541 : }
542 : }
543 :
544 : void
545 570 : odbc_check_rows(int n, int line, const char * file)
546 : {
547 : SQLLEN rows;
548 :
549 570 : if (n < -1) {
550 0 : CHKRowCount(&rows, "E");
551 0 : return;
552 : }
553 :
554 570 : CHKRowCount(&rows, "S");
555 570 : if (rows != n) {
556 0 : fprintf(stderr, "%s:%d: Expected %d rows returned %d\n", file, line, n, (int) rows);
557 0 : odbc_disconnect();
558 0 : exit(1);
559 : }
560 : }
561 :
562 : void
563 16312 : odbc_reset_statement_proc(SQLHSTMT *stmt, const char *file, int line)
564 : {
565 16312 : SQLFreeStmt(*stmt, SQL_DROP);
566 16312 : *stmt = SQL_NULL_HSTMT;
567 16312 : odbc_check_res(file, line, SQLAllocStmt(odbc_conn, stmt), SQL_HANDLE_DBC, odbc_conn, "SQLAllocStmt", "S");
568 16312 : }
569 :
570 : void
571 68 : odbc_test_skipped(void)
572 : {
573 68 : const char *p = getenv("TDS_SKIP_SUCCESS");
574 70 : if (p && atoi(p) != 0)
575 2 : exit(0);
576 66 : exit(77);
577 : }
578 :
579 : void
580 130 : odbc_check_cursor(void)
581 : {
582 : SQLRETURN retcode;
583 130 : ODBC_BUF *odbc_buf = NULL;
584 :
585 130 : retcode = SQLSetStmtAttr(odbc_stmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER) SQL_CONCUR_ROWVER, 0);
586 130 : if (retcode != SQL_SUCCESS) {
587 : SQLTCHAR output[256];
588 : SQLTCHAR sqlstate[6];
589 :
590 26 : CHKGetDiagRec(SQL_HANDLE_STMT, odbc_stmt, 1, sqlstate, NULL, output, TDS_VECTOR_SIZE(output), NULL, "S");
591 26 : sqlstate[5] = 0;
592 26 : if (strcmp(C(sqlstate), "01S02") == 0) {
593 26 : printf("Your connection seems to not support cursors, probably you are using wrong protocol version or Sybase\n");
594 26 : odbc_disconnect();
595 26 : ODBC_FREE();
596 26 : odbc_test_skipped();
597 : }
598 0 : ReportODBCError("SQLSetStmtAttr", SQL_HANDLE_STMT, odbc_stmt, retcode, __LINE__, __FILE__);
599 : }
600 104 : odbc_reset_statement();
601 104 : ODBC_FREE();
602 104 : }
603 :
604 : SQLRETURN
605 142664 : odbc_check_res(const char *file, int line, SQLRETURN rc, SQLSMALLINT handle_type, SQLHANDLE handle, const char *func, const char *res)
606 : {
607 142664 : const char *p = res;
608 : for (;;) {
609 160289 : if (*p == 'S') {
610 124476 : if (rc == SQL_SUCCESS)
611 : return rc;
612 16263 : ++p;
613 35813 : } else if (*p == 'I') {
614 4314 : if (rc == SQL_SUCCESS_WITH_INFO)
615 : return rc;
616 1126 : ++p;
617 31499 : } else if (*p == 'E') {
618 1611 : if (rc == SQL_ERROR)
619 : return rc;
620 234 : ++p;
621 29888 : } else if (strncmp(p, "No", 2) == 0) {
622 29384 : if (rc == SQL_NO_DATA)
623 : return rc;
624 2 : p += 2;
625 504 : } else if (strncmp(p, "Ne", 2) == 0) {
626 464 : if (rc == SQL_NEED_DATA)
627 : return rc;
628 0 : p += 2;
629 40 : } else if (*p == 'V') {
630 40 : if (rc == SQL_INVALID_HANDLE)
631 : return rc;
632 0 : ++p;
633 0 : } else if (!*p) {
634 : break;
635 : } else {
636 0 : odbc_report_error("Wrong results specified", line, file);
637 : return rc;
638 : }
639 : }
640 0 : ReportODBCError(func, handle_type, handle, rc, line, file);
641 : return rc;
642 : }
643 :
644 : SQLSMALLINT
645 184 : odbc_alloc_handle_err_type(SQLSMALLINT type)
646 : {
647 184 : switch (type) {
648 : case SQL_HANDLE_DESC:
649 : return SQL_HANDLE_STMT;
650 : case SQL_HANDLE_STMT:
651 : return SQL_HANDLE_DBC;
652 : case SQL_HANDLE_DBC:
653 : return SQL_HANDLE_ENV;
654 : }
655 : return 0;
656 : }
657 :
658 : SQLRETURN
659 22684 : odbc_command_proc(HSTMT stmt, const char *command, const char *file, int line, const char *res)
660 : {
661 : SQLRETURN ret;
662 22684 : ODBC_BUF *odbc_buf = NULL;
663 :
664 22684 : if (command[0] == '@')
665 40 : ++command;
666 : else
667 22644 : printf("%s\n", command);
668 :
669 22684 : ret = odbc_check_res(file, line, SQLExecDirect(stmt, T(command), SQL_NTS), SQL_HANDLE_STMT, stmt, "odbc_command", res);
670 22684 : ODBC_FREE();
671 22684 : return ret;
672 : }
673 :
674 : char odbc_err[512];
675 : char odbc_sqlstate[6];
676 :
677 : void
678 259 : odbc_read_error(void)
679 : {
680 259 : ODBC_BUF *odbc_buf = NULL;
681 259 : SQLTCHAR *err = (SQLTCHAR *) ODBC_GET(sizeof(odbc_err)*sizeof(SQLTCHAR));
682 259 : SQLTCHAR *state = (SQLTCHAR *) ODBC_GET(sizeof(odbc_sqlstate)*sizeof(SQLTCHAR));
683 :
684 259 : memset(odbc_err, 0, sizeof(odbc_err));
685 259 : memset(odbc_sqlstate, 0, sizeof(odbc_sqlstate));
686 259 : if (odbc_stmt != SQL_NULL_HSTMT)
687 257 : CHKGetDiagRec(SQL_HANDLE_STMT, odbc_stmt, 1, state, NULL, err, sizeof(odbc_err), NULL, "SI");
688 : else
689 2 : CHKGetDiagRec(SQL_HANDLE_DBC, odbc_conn, 1, state, NULL, err, sizeof(odbc_err), NULL, "SI");
690 259 : strcpy(odbc_err, C(err));
691 259 : strcpy(odbc_sqlstate, C(state));
692 259 : ODBC_FREE();
693 259 : printf("Message: '%s' %s\n", odbc_sqlstate, odbc_err);
694 259 : }
695 :
696 : SQLLEN
697 64 : odbc_to_sqlwchar(SQLWCHAR *dst, const char *src, SQLLEN n)
698 : {
699 64 : SQLLEN i = n;
700 1936876 : while (--i >= 0)
701 1919012 : dst[i] = (unsigned char) src[i];
702 64 : return n * sizeof(SQLWCHAR);
703 : }
704 :
705 : SQLLEN
706 4371 : odbc_from_sqlwchar(char *dst, const SQLWCHAR *src, SQLLEN n)
707 : {
708 : SQLLEN i;
709 4371 : if (n < 0) {
710 : const SQLWCHAR *p = src;
711 0 : for (n=1; *p++ != 0; ++n)
712 0 : continue;
713 : }
714 2327830 : for (i = 0; i < n; ++i) {
715 2323459 : assert(src[i] < 256);
716 2323459 : dst[i] = (char) src[i];
717 : }
718 4371 : return n;
719 : }
720 :
721 : ODBC_BUF *odbc_buf = NULL;
722 :
723 : void *
724 23899 : odbc_buf_add(ODBC_BUF** buf, void *ptr)
725 : {
726 23899 : ODBC_BUF *p = (ODBC_BUF*) calloc(1, sizeof(ODBC_BUF));
727 23899 : assert(ptr);
728 23899 : assert(p);
729 23899 : p->buf = ptr;
730 23899 : p->next = *buf;
731 23899 : *buf = p;
732 23899 : return p->buf;
733 : }
734 :
735 : void *
736 1384 : odbc_buf_get(ODBC_BUF** buf, size_t s)
737 : {
738 23449 : return odbc_buf_add(buf, malloc(s));
739 : }
740 :
741 : void
742 45011 : odbc_buf_free(ODBC_BUF** buf)
743 : {
744 45011 : ODBC_BUF *cur = *buf;
745 45011 : *buf = NULL;
746 113921 : while (cur) {
747 23899 : ODBC_BUF *next = cur->next;
748 23899 : free(cur->buf);
749 23899 : free(cur);
750 23899 : cur = next;
751 : }
752 45011 : }
753 :
754 : SQLWCHAR *
755 17904 : odbc_get_sqlwchar(ODBC_BUF** buf, const char *s)
756 : {
757 : size_t l;
758 : SQLWCHAR *buffer;
759 :
760 17904 : if (!s) return NULL;
761 17736 : l = strlen(s) + 1;
762 35472 : buffer = (SQLWCHAR*) odbc_buf_get(buf, l * sizeof(SQLWCHAR));
763 17736 : odbc_to_sqlwchar(buffer, s, l);
764 : return buffer;
765 : }
766 :
767 : char*
768 3811 : odbc_get_sqlchar(ODBC_BUF** buf, SQLWCHAR *s)
769 : {
770 : int n;
771 3811 : const SQLWCHAR *p = s;
772 : char *out;
773 :
774 82899 : for (n=1; *p++ != 0; ++n)
775 79088 : continue;
776 7622 : out = (char *) odbc_buf_get(buf, n);
777 3811 : odbc_from_sqlwchar(out, s, n);
778 3811 : return out;
779 : }
780 :
781 : char *
782 450 : odbc_buf_asprintf(ODBC_BUF** buf, const char *fmt, ...)
783 : {
784 : va_list ap;
785 450 : char *ret = NULL;
786 :
787 450 : va_start(ap, fmt);
788 450 : assert(vasprintf(&ret, fmt, ap) >= 0);
789 450 : va_end(ap);
790 :
791 450 : return (char *) odbc_buf_add(buf, ret);
792 : }
793 :
794 : typedef union {
795 : struct sockaddr sa;
796 : struct sockaddr_in sin;
797 : char dummy[256];
798 : } long_sockaddr;
799 :
800 : static int
801 81680 : fd_is_socket(int fd)
802 : {
803 : long_sockaddr addr;
804 : socklen_t addr_len;
805 :
806 : #ifndef _WIN32
807 : struct stat file_stat;
808 :
809 81680 : if (fstat(fd, &file_stat))
810 : return 0;
811 104 : if ((file_stat.st_mode & S_IFSOCK) != S_IFSOCK)
812 : return 0;
813 : #endif
814 :
815 0 : addr_len = sizeof(addr);
816 0 : if (tds_getpeername((TDS_SYS_SOCKET) fd, &addr.sa, &addr_len))
817 : return 0;
818 :
819 0 : addr_len = sizeof(addr);
820 0 : if (tds_getsockname((TDS_SYS_SOCKET) fd, &addr.sa, &addr_len))
821 : return 0;
822 :
823 0 : return 1;
824 : }
825 :
826 : enum {NUM_FDS = 4096*4};
827 : static unsigned char fd_bitmask[NUM_FDS / 8];
828 :
829 : static int
830 : mark_fd(int fd)
831 : {
832 : unsigned shift;
833 : unsigned char mask;
834 :
835 0 : if (fd < 0 || fd >= NUM_FDS)
836 : return 0;
837 :
838 0 : shift = fd & 7;
839 0 : mask = fd_bitmask[fd >> 3];
840 0 : fd_bitmask[fd >> 3] = mask | (1 << shift);
841 :
842 0 : return (mask >> shift) & 1;
843 : }
844 :
845 : #ifdef _WIN32
846 : #define FOR_ALL_SOCKETS(i) for (i = 4; i <= (4096*4); i += 4)
847 : #else
848 : #define FOR_ALL_SOCKETS(i) for (i = 3; i < 1024; ++i)
849 : #endif
850 :
851 : void
852 80 : odbc_mark_sockets_opened(void)
853 : {
854 : int i;
855 :
856 80 : memset(fd_bitmask, 0, sizeof(fd_bitmask));
857 81760 : FOR_ALL_SOCKETS(i) {
858 81680 : if (fd_is_socket(i))
859 : mark_fd(i);
860 : }
861 80 : }
862 :
863 : TDS_SYS_SOCKET
864 80 : odbc_find_last_socket(void)
865 : {
866 : typedef struct {
867 : TDS_SYS_SOCKET sock;
868 : int local_port;
869 : int remote_port;
870 : } sock_info;
871 : sock_info found[8];
872 80 : unsigned num_found = 0, n;
873 : int i;
874 :
875 80 : SQLULEN sock = 0;
876 : SQLSMALLINT len;
877 80 : if (odbc_driver_is_freetds()
878 80 : && SQLGetInfo(odbc_conn, 1301 /* SQL_INFO_FREETDS_SOCKET */, &sock, sizeof(sock), &len) == SQL_SUCCESS
879 80 : && len == sizeof(sock))
880 80 : return (TDS_SYS_SOCKET) sock;
881 :
882 0 : FOR_ALL_SOCKETS(i) {
883 : long_sockaddr remote_addr, local_addr;
884 : struct sockaddr_in *in;
885 : socklen_t remote_addr_len, local_addr_len;
886 : sock_info *info;
887 :
888 : /* check if is a socket */
889 0 : if (!fd_is_socket(i))
890 0 : continue;
891 0 : if (mark_fd(i))
892 0 : continue;
893 :
894 0 : remote_addr_len = sizeof(remote_addr);
895 0 : if (tds_getpeername((TDS_SYS_SOCKET) i, &remote_addr.sa, &remote_addr_len))
896 0 : continue;
897 0 : if (remote_addr.sa.sa_family != AF_INET
898 : #ifdef AF_INET6
899 0 : && remote_addr.sa.sa_family != AF_INET6
900 : #endif
901 : )
902 0 : continue;
903 0 : local_addr_len = sizeof(local_addr);
904 0 : if (tds_getsockname((TDS_SYS_SOCKET) i, &local_addr.sa, &local_addr_len))
905 0 : continue;
906 :
907 : /* save in the array */
908 0 : if (num_found >= 8) {
909 0 : memmove(found, found+1, sizeof(found) - sizeof(found[0]));
910 0 : num_found = 7;
911 : }
912 0 : info = &found[num_found++];
913 0 : info->sock = (TDS_SYS_SOCKET) i;
914 0 : info->local_port = -1;
915 0 : info->remote_port = -1;
916 :
917 : /* now check if is a socketpair */
918 0 : in = &remote_addr.sin;
919 0 : if (in->sin_family != AF_INET)
920 0 : continue;
921 0 : if (in->sin_addr.s_addr != htonl(INADDR_LOOPBACK))
922 0 : continue;
923 0 : info->remote_port = ntohs(in->sin_port);
924 0 : in = &local_addr.sin;
925 0 : if (in->sin_family != AF_INET)
926 0 : continue;
927 0 : if (in->sin_addr.s_addr != htonl(INADDR_LOOPBACK))
928 0 : continue;
929 0 : info->local_port = ntohs(in->sin_port);
930 0 : for (n = 0; n < num_found - 1; ++n) {
931 0 : if (found[n].remote_port != info->local_port
932 0 : || found[n].local_port != info->remote_port)
933 0 : continue;
934 0 : --num_found;
935 0 : memmove(found+n, found+n+1, num_found-n-1);
936 0 : --num_found;
937 0 : break;
938 : }
939 : }
940 :
941 : /* return last */
942 0 : if (num_found == 0)
943 : return INVALID_SOCKET;
944 0 : return found[num_found-1].sock;
945 : }
946 :
947 : void
948 742 : odbc_check_no_row(const char *query)
949 : {
950 : SQLRETURN rc;
951 :
952 742 : rc = CHKExecDirect(T(query), SQL_NTS, "SINo");
953 742 : if (rc == SQL_NO_DATA)
954 : return;
955 :
956 : do {
957 : SQLSMALLINT cols;
958 :
959 444 : CHKNumResultCols(&cols, "S");
960 444 : if (cols != 0) {
961 0 : fprintf(stderr, "Data not expected here, query:\n\t%s\n", query);
962 0 : odbc_disconnect();
963 0 : exit(1);
964 : }
965 444 : } while (CHKMoreResults("SNo") == SQL_SUCCESS);
966 : }
967 :
968 : int
969 1360 : odbc_lookup(const char *name, const struct odbc_lookup_int *table, int def)
970 : {
971 3790 : for (; table->name; ++table)
972 3790 : if (strcmp(table->name, name) == 0)
973 1360 : return table->value;
974 :
975 : return def;
976 : }
977 :
978 : const char*
979 250 : odbc_lookup_value(int value, const struct odbc_lookup_int *table, const char *def)
980 : {
981 4700 : for (; table->name; ++table)
982 4700 : if (table->value == value)
983 : return table->name;
984 :
985 : return def;
986 : }
987 :
988 : struct odbc_lookup_int odbc_sql_c_types[] = {
989 : #define TYPE(s) { #s, s }
990 : TYPE(SQL_C_NUMERIC),
991 : TYPE(SQL_C_BINARY),
992 : TYPE(SQL_C_CHAR),
993 : TYPE(SQL_C_WCHAR),
994 : TYPE(SQL_C_LONG),
995 : TYPE(SQL_C_SBIGINT),
996 : TYPE(SQL_C_SHORT),
997 : TYPE(SQL_C_TIMESTAMP),
998 : TYPE(SQL_C_FLOAT),
999 : TYPE(SQL_C_DOUBLE),
1000 : TYPE(SQL_C_DEFAULT),
1001 : TYPE(SQL_C_DATE),
1002 : TYPE(SQL_C_TIME),
1003 : TYPE(SQL_C_TYPE_DATE),
1004 : TYPE(SQL_C_TYPE_TIME),
1005 : TYPE(SQL_C_TYPE_TIMESTAMP),
1006 : TYPE(SQL_C_INTERVAL_YEAR),
1007 : TYPE(SQL_C_INTERVAL_MONTH),
1008 : TYPE(SQL_C_INTERVAL_DAY),
1009 : TYPE(SQL_C_INTERVAL_HOUR),
1010 : TYPE(SQL_C_INTERVAL_MINUTE),
1011 : TYPE(SQL_C_INTERVAL_SECOND),
1012 : TYPE(SQL_C_INTERVAL_YEAR_TO_MONTH),
1013 : TYPE(SQL_C_INTERVAL_DAY_TO_HOUR),
1014 : TYPE(SQL_C_INTERVAL_DAY_TO_MINUTE),
1015 : TYPE(SQL_C_INTERVAL_DAY_TO_SECOND),
1016 : TYPE(SQL_C_INTERVAL_HOUR_TO_MINUTE),
1017 : TYPE(SQL_C_INTERVAL_HOUR_TO_SECOND),
1018 : TYPE(SQL_C_INTERVAL_MINUTE_TO_SECOND),
1019 : TYPE(SQL_C_BIT),
1020 : TYPE(SQL_C_UBIGINT),
1021 : TYPE(SQL_C_TINYINT),
1022 : TYPE(SQL_C_SLONG),
1023 : TYPE(SQL_C_SSHORT),
1024 : TYPE(SQL_C_STINYINT),
1025 : TYPE(SQL_C_ULONG),
1026 : TYPE(SQL_C_USHORT),
1027 : TYPE(SQL_C_UTINYINT),
1028 : TYPE(SQL_C_GUID),
1029 : #undef TYPE
1030 : { NULL, 0 }
1031 : };
1032 :
1033 : struct odbc_lookup_int odbc_sql_types[] = {
1034 : #define TYPE(s) { #s, s }
1035 : TYPE(SQL_CHAR),
1036 : TYPE(SQL_VARCHAR),
1037 : TYPE(SQL_LONGVARCHAR),
1038 : TYPE(SQL_WCHAR),
1039 : TYPE(SQL_WVARCHAR),
1040 : TYPE(SQL_WLONGVARCHAR),
1041 : TYPE(SQL_DECIMAL),
1042 : TYPE(SQL_NUMERIC),
1043 : TYPE(SQL_SMALLINT),
1044 : TYPE(SQL_INTEGER),
1045 : TYPE(SQL_REAL),
1046 : TYPE(SQL_FLOAT),
1047 : TYPE(SQL_DOUBLE),
1048 : TYPE(SQL_BIT),
1049 : TYPE(SQL_TINYINT),
1050 : TYPE(SQL_BIGINT),
1051 : TYPE(SQL_BINARY),
1052 : TYPE(SQL_VARBINARY),
1053 : TYPE(SQL_LONGVARBINARY),
1054 : TYPE(SQL_DATE),
1055 : TYPE(SQL_TIME),
1056 : TYPE(SQL_TIMESTAMP),
1057 : TYPE(SQL_TYPE_DATE),
1058 : TYPE(SQL_TYPE_TIME),
1059 : TYPE(SQL_TYPE_TIMESTAMP),
1060 : TYPE(SQL_DATETIME),
1061 : TYPE(SQL_SS_VARIANT),
1062 : TYPE(SQL_SS_UDT),
1063 : TYPE(SQL_SS_XML),
1064 : TYPE(SQL_SS_TABLE),
1065 : TYPE(SQL_SS_TIME2),
1066 : TYPE(SQL_SS_TIMESTAMPOFFSET),
1067 : #ifdef SQL_GUID
1068 : TYPE(SQL_GUID),
1069 : #endif
1070 : #undef TYPE
1071 : { NULL, 0 }
1072 : };
1073 :
1074 : #ifdef _MSC_VER
1075 : /* See https://learn.microsoft.com/en-us/cpp/preprocessor/warning?view=msvc-170 */
1076 : #pragma warning(push)
1077 : #pragma warning(disable:4996)
1078 : #endif
1079 : SQLRETURN
1080 8 : SQLSetStmtOption_nowarning(SQLHSTMT hstmt, SQLSMALLINT option, SQLULEN param)
1081 : {
1082 8 : return SQLSetStmtOption(hstmt, option, param);
1083 : }
1084 : #ifdef _MSC_VER
1085 : #pragma warning(pop)
1086 : #endif
1087 :
1088 : void
1089 866 : odbc_swap_stmts(SQLHSTMT *a, SQLHSTMT *b)
1090 : {
1091 866 : SQLHSTMT tmp = *a;
1092 866 : *a = *b;
1093 866 : *b = tmp;
1094 866 : }
|