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