1/* 2 * info.c 3 * 4 * $Id: info.c,v 1.36 2007/01/05 12:22:39 source Exp $ 5 * 6 * Information functions 7 * 8 * The iODBC driver manager. 9 * 10 * Copyright (C) 1995 by Ke Jin <kejin@empress.com> 11 * Copyright (C) 1996-2006 by OpenLink Software <iodbc@openlinksw.com> 12 * All Rights Reserved. 13 * 14 * This software is released under the terms of either of the following 15 * licenses: 16 * 17 * - GNU Library General Public License (see LICENSE.LGPL) 18 * - The BSD License (see LICENSE.BSD). 19 * 20 * Note that the only valid version of the LGPL license as far as this 21 * project is concerned is the original GNU Library General Public License 22 * Version 2, dated June 1991. 23 * 24 * While not mandated by the BSD license, any patches you make to the 25 * iODBC source code may be contributed back into the iODBC project 26 * at your discretion. Contributions will benefit the Open Source and 27 * Data Access community as a whole. Submissions may be made at: 28 * 29 * http://www.iodbc.org 30 * 31 * 32 * GNU Library Generic Public License Version 2 33 * ============================================ 34 * This library is free software; you can redistribute it and/or 35 * modify it under the terms of the GNU Library General Public 36 * License as published by the Free Software Foundation; only 37 * Version 2 of the License dated June 1991. 38 * 39 * This library is distributed in the hope that it will be useful, 40 * but WITHOUT ANY WARRANTY; without even the implied warranty of 41 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 42 * Library General Public License for more details. 43 * 44 * You should have received a copy of the GNU Library General Public 45 * License along with this library; if not, write to the Free 46 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 47 * 48 * 49 * The BSD License 50 * =============== 51 * Redistribution and use in source and binary forms, with or without 52 * modification, are permitted provided that the following conditions 53 * are met: 54 * 55 * 1. Redistributions of source code must retain the above copyright 56 * notice, this list of conditions and the following disclaimer. 57 * 2. Redistributions in binary form must reproduce the above copyright 58 * notice, this list of conditions and the following disclaimer in 59 * the documentation and/or other materials provided with the 60 * distribution. 61 * 3. Neither the name of OpenLink Software Inc. nor the names of its 62 * contributors may be used to endorse or promote products derived 63 * from this software without specific prior written permission. 64 * 65 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 66 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 67 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 68 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OPENLINK OR 69 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 70 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 71 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 72 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 73 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 74 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 75 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 76 */ 77 78 79#include <iodbc.h> 80 81#include <sql.h> 82#include <sqlext.h> 83#include <sqlucode.h> 84#include <odbcinst.h> 85 86#include <unicode.h> 87 88#include <dlproc.h> 89 90#include <herr.h> 91#include <henv.h> 92#include <hdbc.h> 93#include <hstmt.h> 94 95#include <itrace.h> 96 97#include <stdio.h> 98#include <ctype.h> 99 100#ifdef WIN32 101#define SECT1 "ODBC 32 bit Data Sources" 102#define SECT2 "ODBC 32 bit Drivers" 103#else 104#define SECT1 "ODBC Data Sources" 105#define SECT2 "ODBC Drivers" 106#endif 107 108#define MAX_ENTRIES 1024 109 110 111static int 112stricmp (const char *s1, const char *s2) 113{ 114 int cmp; 115 116 while (*s1) 117 { 118 if ((cmp = toupper (*s1) - toupper (*s2)) != 0) 119 return cmp; 120 s1++; 121 s2++; 122 } 123 return (*s2) ? -1 : 0; 124} 125 126 127static int 128SectSorter (const void *p1, const void *p2) 129{ 130 const char **s1 = (const char **) p1; 131 const char **s2 = (const char **) p2; 132 133 return stricmp (*s1, *s2); 134} 135 136 137SQLRETURN SQL_API 138SQLDataSources_Internal ( 139 SQLHENV henv, 140 SQLUSMALLINT fDir, 141 SQLPOINTER szDSN, 142 SQLSMALLINT cbDSNMax, 143 SQLSMALLINT * pcbDSN, 144 SQLPOINTER szDesc, 145 SQLSMALLINT cbDescMax, 146 SQLSMALLINT * pcbDesc, 147 SQLCHAR waMode) 148{ 149 GENV (genv, henv); 150 char buffer[4096], desc[1024], *ptr; 151 int i, j, usernum = 0; 152 static int cur_entry = -1; 153 static int num_entries = 0; 154 static void **sect = NULL; 155 SQLUSMALLINT fDirOld = fDir; 156 157 waMode = waMode; /*UNUSED*/ 158 159 /* check argument */ 160 if (cbDSNMax < 0 || cbDescMax < 0) 161 { 162 PUSHSQLERR (genv->herr, en_S1090); 163 return SQL_ERROR; 164 } 165 166 if (fDir != SQL_FETCH_FIRST && fDir != SQL_FETCH_NEXT && 167 fDir != SQL_FETCH_FIRST_USER && fDir != SQL_FETCH_FIRST_SYSTEM) 168 { 169 PUSHSQLERR (genv->herr, en_S1103); 170 return SQL_ERROR; 171 } 172 173 if (cur_entry < 0 || fDir == SQL_FETCH_FIRST || 174 fDir == SQL_FETCH_FIRST_USER || fDir == SQL_FETCH_FIRST_SYSTEM) 175 { 176 cur_entry = 0; 177 num_entries = 0; 178 179 /* 180 * Free old section list 181 */ 182 if (sect) 183 { 184 for (i = 0; i < MAX_ENTRIES; i++) 185 if (sect[i]) 186 free (sect[i]); 187 free (sect); 188 } 189 if ((sect = (void **) calloc (MAX_ENTRIES, sizeof (void *))) == NULL) 190 { 191 PUSHSQLERR (genv->herr, en_S1011); 192 return SQL_ERROR; 193 } 194 195 if (fDirOld == SQL_FETCH_FIRST) 196 fDir = SQL_FETCH_FIRST_USER; 197 198 do { 199 SQLSetConfigMode (fDir == SQL_FETCH_FIRST_SYSTEM ? ODBC_SYSTEM_DSN : ODBC_USER_DSN); 200 SQLGetPrivateProfileString (SECT1, NULL, "", buffer, sizeof(buffer) / sizeof(SQLTCHAR), "odbc.ini"); 201 202 /* For each datasources */ 203 for(ptr = buffer, i = 1 ; *ptr && i ; ptr += STRLEN(ptr) + 1) 204 { 205 /* Add this section to the datasources list */ 206 if (fDirOld == SQL_FETCH_FIRST && fDir == SQL_FETCH_FIRST_SYSTEM) 207 { 208 for(j = 0 ; j<usernum ; j++) 209 { 210 if(STREQ(sect[j<<1], ptr)) 211 j = usernum; 212 } 213 if(j == usernum + 1) 214 continue; 215 } 216 217 if ((num_entries << 1) >= MAX_ENTRIES) 218 { 219 i = 0; 220 break; 221 } /* Skip the rest */ 222 223 /* Copy the datasource name */ 224 sect[num_entries<<1] = STRDUP (ptr); 225 226 /* ... and its description */ 227 SQLSetConfigMode (fDir == SQL_FETCH_FIRST_SYSTEM ? ODBC_SYSTEM_DSN : ODBC_USER_DSN); 228 SQLGetPrivateProfileString (SECT1, ptr, "", desc, sizeof(desc) / sizeof(SQLTCHAR), "odbc.ini"); 229 sect[(num_entries++<<1) + 1] = STRDUP (desc); 230 } 231 232 switch(fDir) 233 { 234 case SQL_FETCH_FIRST_USER: 235 fDir = SQL_FETCH_FIRST_SYSTEM; 236 usernum = num_entries; 237 break; 238 case SQL_FETCH_FIRST_SYSTEM: 239 fDir = SQL_FETCH_FIRST; 240 break; 241 }; 242 } while (fDir!=SQL_FETCH_FIRST && fDirOld==SQL_FETCH_FIRST); 243 244 fDir = fDirOld; 245 246 /* 247 * Sort all entries so we can present a nice list 248 */ 249 if (num_entries > 1) 250 { 251 qsort (sect, num_entries, sizeof (char **) + sizeof (char **), 252 SectSorter); 253 } 254 } 255 256 /* 257 * Try to get to the next item 258 */ 259 if (cur_entry >= num_entries) 260 { 261 cur_entry = 0; /* Next time, start all over again */ 262 return SQL_NO_DATA_FOUND; 263 } 264 265 /* 266 * Copy DSN information 267 */ 268 STRNCPY (szDSN, sect[cur_entry << 1], cbDSNMax); 269 270 if (pcbDSN) 271 *pcbDSN = STRLEN (szDSN); 272 273 /* 274 * And find the description that goes with this entry 275 */ 276 STRNCPY (szDesc, sect[(cur_entry << 1) + 1], cbDescMax); 277 278 if (pcbDesc) 279 *pcbDesc = STRLEN (szDesc); 280 281 /* 282 * Next record 283 */ 284 cur_entry++; 285 286 return SQL_SUCCESS; 287} 288 289 290SQLRETURN SQL_API 291SQLDataSources ( 292 SQLHENV henv, 293 SQLUSMALLINT fDir, 294 SQLCHAR * szDSN, 295 SQLSMALLINT cbDSNMax, 296 SQLSMALLINT * pcbDSN, 297 SQLCHAR * szDesc, 298 SQLSMALLINT cbDescMax, 299 SQLSMALLINT * pcbDesc) 300{ 301 ENTER_HENV (henv, 302 trace_SQLDataSources (TRACE_ENTER, 303 henv, 304 fDir, 305 szDSN, cbDSNMax, pcbDSN, 306 szDesc, cbDescMax, pcbDesc)); 307 308 retcode = SQLDataSources_Internal ( 309 henv, 310 fDir, 311 szDSN, cbDSNMax, pcbDSN, 312 szDesc, cbDescMax, pcbDesc, 313 'A'); 314 315 LEAVE_HENV (henv, 316 trace_SQLDataSources (TRACE_LEAVE, 317 henv, 318 fDir, 319 szDSN, cbDSNMax, pcbDSN, 320 szDesc, cbDescMax, pcbDesc)); 321} 322 323 324#if ODBCVER >= 0x0300 325SQLRETURN SQL_API 326SQLDataSourcesA ( 327 SQLHENV henv, 328 SQLUSMALLINT fDir, 329 SQLCHAR * szDSN, 330 SQLSMALLINT cbDSNMax, 331 SQLSMALLINT * pcbDSN, 332 SQLCHAR * szDesc, 333 SQLSMALLINT cbDescMax, 334 SQLSMALLINT * pcbDesc) 335{ 336 ENTER_HENV (henv, 337 trace_SQLDataSources (TRACE_ENTER, 338 henv, 339 fDir, 340 szDSN, cbDSNMax, pcbDSN, 341 szDesc, cbDescMax, pcbDesc)); 342 343 retcode = SQLDataSources_Internal( 344 henv, 345 fDir, 346 szDSN, cbDSNMax, pcbDSN, 347 szDesc, cbDescMax, pcbDesc, 348 'A'); 349 350 LEAVE_HENV (henv, 351 trace_SQLDataSources (TRACE_LEAVE, 352 henv, 353 fDir, 354 szDSN, cbDSNMax, pcbDSN, 355 szDesc, cbDescMax, pcbDesc)); 356} 357 358 359SQLRETURN SQL_API 360SQLDataSourcesW ( 361 SQLHENV henv, 362 SQLUSMALLINT fDir, 363 SQLWCHAR * szDSN, 364 SQLSMALLINT cbDSNMax, 365 SQLSMALLINT * pcbDSN, 366 SQLWCHAR * szDesc, 367 SQLSMALLINT cbDescMax, 368 SQLSMALLINT * pcbDesc) 369{ 370 SQLCHAR *_DSN = NULL; 371 SQLCHAR *_Desc = NULL; 372 373 ENTER_HENV (henv, 374 trace_SQLDataSourcesW (TRACE_ENTER, 375 henv, 376 fDir, 377 szDSN, cbDSNMax, pcbDSN, 378 szDesc, cbDescMax, pcbDesc)); 379 380 if (cbDSNMax > 0) 381 { 382 if ((_DSN = (SQLCHAR *) malloc (cbDSNMax * UTF8_MAX_CHAR_LEN + 1)) == NULL) 383 { 384 PUSHSQLERR (genv->herr, en_S1001); 385 return SQL_ERROR; 386 } 387 } 388 389 if (cbDescMax > 0) 390 { 391 if ((_Desc = (SQLCHAR *) malloc (cbDescMax * UTF8_MAX_CHAR_LEN + 1)) == NULL) 392 { 393 PUSHSQLERR (genv->herr, en_S1001); 394 return SQL_ERROR; 395 } 396 } 397 398 retcode = SQLDataSources_Internal ( 399 henv, 400 fDir, 401 _DSN, cbDSNMax * UTF8_MAX_CHAR_LEN, pcbDSN, 402 _Desc, cbDescMax * UTF8_MAX_CHAR_LEN, pcbDesc, 403 'W'); 404 405 if (SQL_SUCCEEDED (retcode)) 406 { 407 dm_StrCopyOut2_U8toW (_DSN, szDSN, cbDSNMax, pcbDSN); 408 dm_StrCopyOut2_U8toW (_Desc, szDesc, cbDescMax, pcbDesc); 409 } 410 411 MEM_FREE (_DSN); 412 MEM_FREE (_Desc); 413 414 LEAVE_HENV (henv, 415 trace_SQLDataSourcesW (TRACE_LEAVE, 416 henv, 417 fDir, 418 szDSN, cbDSNMax, pcbDSN, 419 szDesc, cbDescMax, pcbDesc)); 420} 421#endif 422 423 424SQLRETURN SQL_API 425SQLDrivers_Internal ( 426 SQLHENV henv, 427 SQLUSMALLINT fDir, 428 SQLPOINTER szDrvDesc, 429 SQLSMALLINT cbDrvDescMax, 430 SQLSMALLINT * pcbDrvDesc, 431 SQLPOINTER szDrvAttr, 432 SQLSMALLINT cbDrvAttrMax, 433 SQLSMALLINT * pcbDrvAttr, 434 SQLCHAR waMode) 435{ 436 GENV (genv, henv); 437 char buffer[4096], desc[1024], *ptr; 438 int i, j, usernum = 0; 439 static int cur_entry = -1; 440 static int num_entries = 0; 441 static void **sect = NULL; 442 SQLUSMALLINT fDirOld = fDir; 443 444 waMode = waMode; /*UNUSED*/ 445 446 /* check argument */ 447 if (cbDrvDescMax < 0 || cbDrvAttrMax < 0) 448 { 449 PUSHSQLERR (genv->herr, en_S1090); 450 return SQL_ERROR; 451 } 452 453 if (fDir != SQL_FETCH_FIRST && fDir != SQL_FETCH_NEXT) 454 { 455 PUSHSQLERR (genv->herr, en_S1103); 456 return SQL_ERROR; 457 } 458 459 if (cur_entry < 0 || fDir == SQL_FETCH_FIRST) 460 { 461 cur_entry = 0; 462 num_entries = 0; 463 464 /* 465 * Free old section list 466 */ 467 if (sect) 468 { 469 for (i = 0; i < MAX_ENTRIES; i++) 470 if (sect[i]) 471 free (sect[i]); 472 free (sect); 473 } 474 if ((sect = (void **) calloc (MAX_ENTRIES, sizeof (void *))) == NULL) 475 { 476 PUSHSQLERR (genv->herr, en_S1011); 477 return SQL_ERROR; 478 } 479 480 if (fDirOld == SQL_FETCH_FIRST) 481 fDir = SQL_FETCH_FIRST_USER; 482 483 do { 484 SQLSetConfigMode (fDir == SQL_FETCH_FIRST_SYSTEM ? ODBC_SYSTEM_DSN : ODBC_USER_DSN); 485 SQLGetPrivateProfileString (SECT2, NULL, "", buffer, sizeof(buffer) / sizeof(SQLTCHAR), "odbcinst.ini"); 486 487 /* For each datasources */ 488 for(ptr = buffer, i = 1 ; *ptr && i ; ptr += STRLEN(ptr) + 1) 489 { 490 /* Add this section to the datasources list */ 491 if (fDirOld == SQL_FETCH_FIRST && fDir == SQL_FETCH_FIRST_SYSTEM) 492 { 493 for(j = 0 ; j<usernum ; j++) 494 { 495 if(STREQ(sect[j<<1], ptr)) 496 j = usernum; 497 } 498 if(j == usernum + 1) 499 continue; 500 } 501 502 if ((num_entries << 1) >= MAX_ENTRIES) 503 { 504 i = 0; 505 break; 506 } /* Skip the rest */ 507 508 /* ... and its description */ 509 SQLSetConfigMode (fDir == SQL_FETCH_FIRST_SYSTEM ? ODBC_SYSTEM_DSN : ODBC_USER_DSN); 510 SQLGetPrivateProfileString (SECT2, ptr, "", desc, sizeof(desc) / sizeof(SQLTCHAR), "odbcinst.ini"); 511 512 /* Check if the driver is installed */ 513 if(!STRCASEEQ(desc, "Installed")) 514 continue; 515 516 /* Copy the driver name */ 517 sect[num_entries<<1] = STRDUP (ptr); 518 sect[(num_entries++<<1) + 1] = STRDUP (desc); 519 } 520 521 switch(fDir) 522 { 523 case SQL_FETCH_FIRST_USER: 524 fDir = SQL_FETCH_FIRST_SYSTEM; 525 usernum = num_entries; 526 break; 527 case SQL_FETCH_FIRST_SYSTEM: 528 fDir = SQL_FETCH_FIRST; 529 break; 530 }; 531 } while (fDir!=SQL_FETCH_FIRST && fDirOld==SQL_FETCH_FIRST); 532 533 fDir = fDirOld; 534 535 /* 536 * Sort all entries so we can present a nice list 537 */ 538 if (num_entries > 1) 539 { 540 qsort (sect, num_entries, sizeof (char **) + sizeof (char **), 541 SectSorter); 542 } 543 } 544 545 /* 546 * Try to get to the next item 547 */ 548 if (cur_entry >= num_entries) 549 { 550 cur_entry = 0; /* Next time, start all over again */ 551 return SQL_NO_DATA_FOUND; 552 } 553 554 /* 555 * Copy Driver information 556 */ 557 STRNCPY (szDrvDesc, sect[cur_entry << 1], cbDrvDescMax); 558 559 if (pcbDrvDesc) 560 *pcbDrvDesc = STRLEN (szDrvDesc); 561 562 /* 563 * And find the description that goes with this entry 564 */ 565 STRNCPY (szDrvAttr, sect[(cur_entry << 1) + 1], cbDrvAttrMax); 566 567 if (pcbDrvAttr) 568 *pcbDrvAttr = STRLEN (szDrvAttr); 569 570 /* 571 * Next record 572 */ 573 cur_entry++; 574 575 return SQL_SUCCESS; 576} 577 578 579SQLRETURN SQL_API 580SQLDrivers ( 581 SQLHENV henv, 582 SQLUSMALLINT fDir, 583 SQLCHAR * szDrvDesc, 584 SQLSMALLINT cbDrvDescMax, 585 SQLSMALLINT * pcbDrvDesc, 586 SQLCHAR * szDrvAttr, 587 SQLSMALLINT cbDrvAttrMax, 588 SQLSMALLINT * pcbDrvAttr) 589{ 590 ENTER_HENV (henv, 591 trace_SQLDrivers (TRACE_ENTER, 592 henv, 593 fDir, 594 szDrvDesc, cbDrvDescMax, pcbDrvDesc, 595 szDrvAttr, cbDrvAttrMax, pcbDrvAttr)); 596 597 retcode = SQLDrivers_Internal( 598 henv, 599 fDir, 600 szDrvDesc, cbDrvDescMax, pcbDrvDesc, 601 szDrvAttr, cbDrvAttrMax, pcbDrvAttr, 602 'A'); 603 604 LEAVE_HENV (henv, 605 trace_SQLDrivers (TRACE_LEAVE, 606 henv, 607 fDir, 608 szDrvDesc, cbDrvDescMax, pcbDrvDesc, 609 szDrvAttr, cbDrvAttrMax, pcbDrvAttr)); 610} 611 612 613#if ODBCVER >= 0x0300 614SQLRETURN SQL_API 615SQLDriversA ( 616 SQLHENV henv, 617 SQLUSMALLINT fDir, 618 SQLCHAR * szDrvDesc, 619 SQLSMALLINT cbDrvDescMax, 620 SQLSMALLINT * pcbDrvDesc, 621 SQLCHAR * szDrvAttr, 622 SQLSMALLINT cbDrvAttrMax, 623 SQLSMALLINT * pcbDrvAttr) 624{ 625 ENTER_HENV (henv, 626 trace_SQLDrivers (TRACE_ENTER, 627 henv, 628 fDir, 629 szDrvDesc, cbDrvDescMax, pcbDrvDesc, 630 szDrvAttr, cbDrvAttrMax, pcbDrvAttr)); 631 632 retcode = SQLDrivers_Internal( 633 henv, 634 fDir, 635 szDrvDesc, cbDrvDescMax, pcbDrvDesc, 636 szDrvAttr, cbDrvAttrMax, pcbDrvAttr, 637 'A'); 638 639 LEAVE_HENV (henv, 640 trace_SQLDrivers (TRACE_LEAVE, 641 henv, 642 fDir, 643 szDrvDesc, cbDrvDescMax, pcbDrvDesc, 644 szDrvAttr, cbDrvAttrMax, pcbDrvAttr)); 645} 646 647 648SQLRETURN SQL_API 649SQLDriversW (SQLHENV henv, 650 SQLUSMALLINT fDir, 651 SQLWCHAR * szDrvDesc, 652 SQLSMALLINT cbDrvDescMax, 653 SQLSMALLINT * pcbDrvDesc, 654 SQLWCHAR * szDrvAttr, 655 SQLSMALLINT cbDrvAttrMax, 656 SQLSMALLINT * pcbDrvAttr) 657{ 658 SQLCHAR *_Driver = NULL; 659 SQLCHAR *_Attrs = NULL; 660 661 ENTER_HENV (henv, 662 trace_SQLDriversW (TRACE_ENTER, 663 henv, 664 fDir, 665 szDrvDesc, cbDrvDescMax, pcbDrvDesc, 666 szDrvAttr, cbDrvAttrMax, pcbDrvAttr)); 667 668 if (cbDrvDescMax > 0) 669 { 670 if ((_Driver = (SQLCHAR *) malloc (cbDrvDescMax * UTF8_MAX_CHAR_LEN + 1)) == NULL) 671 { 672 PUSHSQLERR (genv->herr, en_S1001); 673 return SQL_ERROR; 674 } 675 } 676 677 if (cbDrvAttrMax > 0) 678 { 679 if ((_Attrs = (SQLCHAR *) malloc (cbDrvAttrMax * UTF8_MAX_CHAR_LEN + 1)) == NULL) 680 { 681 PUSHSQLERR (genv->herr, en_S1001); 682 return SQL_ERROR; 683 } 684 } 685 686 retcode = SQLDrivers_Internal ( 687 henv, 688 fDir, 689 _Driver, cbDrvDescMax * UTF8_MAX_CHAR_LEN, pcbDrvDesc, 690 _Attrs, cbDrvAttrMax * UTF8_MAX_CHAR_LEN, pcbDrvAttr, 691 'W'); 692 693 if (SQL_SUCCEEDED (retcode)) 694 { 695 dm_StrCopyOut2_U8toW (_Driver, szDrvDesc, cbDrvDescMax, pcbDrvDesc); 696 dm_StrCopyOut2_U8toW (_Attrs, szDrvAttr, cbDrvAttrMax, pcbDrvAttr); 697 } 698 699 MEM_FREE (_Driver); 700 MEM_FREE (_Attrs); 701 702 LEAVE_HENV (henv, 703 trace_SQLDriversW (TRACE_LEAVE, 704 henv, 705 fDir, 706 szDrvDesc, cbDrvDescMax, pcbDrvDesc, 707 szDrvAttr, cbDrvAttrMax, pcbDrvAttr)); 708} 709#endif 710 711 712SQLRETURN SQL_API 713SQLGetInfo_Internal ( 714 SQLHDBC hdbc, 715 SQLUSMALLINT fInfoType, 716 SQLPOINTER rgbInfoValue, 717 SQLSMALLINT cbInfoValueMax, 718 SQLSMALLINT * pcbInfoValue, 719 SQLCHAR waMode) 720{ 721 CONN (pdbc, hdbc); 722 ENVR (penv, pdbc->henv); 723 STMT (pstmt, NULL); 724 STMT (tpstmt, NULL); 725 HPROC hproc = SQL_NULL_HPROC; 726 SQLRETURN retcode = SQL_SUCCESS; 727 void * _InfoValue = NULL; 728 void * infoValueOut = rgbInfoValue; 729 730 DWORD dword = 0; 731 int size = 0, len = 0, ret = 0; 732 wchar_t buf[20] = {'\0'}; 733 734 if (cbInfoValueMax < 0) 735 { 736 PUSHSQLERR (pdbc->herr, en_S1090); 737 return SQL_ERROR; 738 } 739 740#if (ODBCVER < 0x0300) 741 if ( /* fInfoType < SQL_INFO_FIRST || */ 742 (fInfoType > SQL_INFO_LAST 743 && fInfoType < SQL_INFO_DRIVER_START)) 744 { 745 PUSHSQLERR (pdbc->herr, en_S1096); 746 return SQL_ERROR; 747 } 748#endif 749 if (fInfoType == SQL_ODBC_VER 750#if (ODBCVER >= 0x0300) 751 || fInfoType == SQL_DM_VER 752#endif 753 ) 754 { 755#if (ODBCVER >= 0x0300) 756 if (fInfoType == SQL_DM_VER) 757 sprintf ((char*)buf, "%02d.%02d.%04d.%04d", 758 SQL_SPEC_MAJOR, SQL_SPEC_MINOR, IODBC_BUILD / 10000, IODBC_BUILD % 10000); 759 else 760#endif 761 sprintf ((char*)buf, "%02d.%02d.0000", SQL_SPEC_MAJOR, SQL_SPEC_MINOR); 762 if(waMode == 'W') 763 { 764 SQLWCHAR *prov = dm_SQL_U8toW((SQLCHAR *)buf, SQL_NTS); 765 if(prov) 766 { 767 WCSNCPY(buf, prov, sizeof(buf)/sizeof(wchar_t)); 768 free(prov); 769 } 770 else 771 buf[0] = L'\0'; 772 } 773 774 775 if (rgbInfoValue != NULL && cbInfoValueMax > 0) 776 { 777 len = (waMode != 'W' ? STRLEN (buf) : WCSLEN(buf)); 778 779 if (len > cbInfoValueMax - 1) 780 { 781 len = cbInfoValueMax - 1; 782 PUSHSQLERR (pdbc->herr, en_01004); 783 784 retcode = SQL_SUCCESS_WITH_INFO; 785 } 786 787 if (waMode != 'W') 788 { 789 STRNCPY (rgbInfoValue, buf, len); 790 ((char *) rgbInfoValue)[len] = '\0'; 791 } 792 else 793 { 794 WCSNCPY (rgbInfoValue, buf, len); 795 ((wchar_t *) rgbInfoValue)[len] = L'\0'; 796 } 797 } 798 799 if (pcbInfoValue != NULL) 800 { 801 *pcbInfoValue = (SWORD) len; 802 } 803 804 return retcode; 805 } 806 807 if (pdbc->state == en_dbc_allocated || pdbc->state == en_dbc_needdata) 808 { 809 PUSHSQLERR (pdbc->herr, en_08003); 810 811 return SQL_ERROR; 812 } 813 814 switch (fInfoType) 815 { 816 case SQL_DRIVER_HDBC: 817 dword = (DWORD) (pdbc->dhdbc); 818 size = sizeof (dword); 819 break; 820 821 case SQL_DRIVER_HENV: 822 penv = (ENV_t *) (pdbc->henv); 823 dword = (DWORD) (penv->dhenv); 824 size = sizeof (dword); 825 break; 826 827 case SQL_DRIVER_HLIB: 828 penv = (ENV_t *) (pdbc->henv); 829 dword = (DWORD) (penv->hdll); 830 size = sizeof (dword); 831 break; 832 833 case SQL_DRIVER_HSTMT: 834 if (rgbInfoValue != NULL) 835 { 836 pstmt = *((STMT_t **) rgbInfoValue); 837 } 838 839 for (tpstmt = (STMT_t *) (pdbc->hstmt); 840 tpstmt != NULL; 841 tpstmt = tpstmt->next) 842 { 843 if (tpstmt == pstmt) 844 { 845 break; 846 } 847 } 848 849 if (tpstmt == NULL) 850 { 851 PUSHSQLERR (pdbc->herr, en_S1009); 852 853 return SQL_ERROR; 854 } 855 856 dword = (DWORD) (pstmt->dhstmt); 857 size = sizeof (dword); 858 break; 859 860 case SQL_DRIVER_NAME: 861 case SQL_DRIVER_ODBC_VER: 862 case SQL_DRIVER_VER: 863 case SQL_ODBC_INTERFACE_CONFORMANCE: 864 break; 865 866 default: 867 /* NOTE : this was before the switch, just move here to let some informations going through */ 868 if (pdbc->state == en_dbc_allocated || pdbc->state == en_dbc_needdata) 869 { 870 PUSHSQLERR (pdbc->herr, en_08003); 871 return SQL_ERROR; 872 } 873 } 874 875 if (size) 876 { 877 if (rgbInfoValue != NULL) 878 { 879 *((DWORD *) rgbInfoValue) = dword; 880 } 881 882 if (pcbInfoValue != NULL) 883 { 884 *(pcbInfoValue) = (SWORD) size; 885 } 886 887 return SQL_SUCCESS; 888 } 889 890#if (ODBCVER >= 0x0300) 891 /* 892 * This was a temp value in ODBC 2 893 */ 894 if (((ENV_t *) pdbc->henv)->dodbc_ver == SQL_OV_ODBC2 && 895 fInfoType == SQL_OJ_CAPABILITIES) 896 fInfoType = 65003; 897#endif /* ODBCVER >= 0x0300 */ 898 899 if ((penv->unicode_driver && waMode != 'W') 900 || (!penv->unicode_driver && waMode == 'W')) 901 { 902 switch(fInfoType) 903 { 904 case SQL_ACCESSIBLE_PROCEDURES: 905 case SQL_ACCESSIBLE_TABLES: 906 case SQL_CATALOG_NAME: 907 case SQL_CATALOG_NAME_SEPARATOR: 908 case SQL_CATALOG_TERM: 909 case SQL_COLLATION_SEQ: 910 case SQL_COLUMN_ALIAS: 911 case SQL_DATA_SOURCE_NAME: 912 case SQL_DATA_SOURCE_READ_ONLY: 913 case SQL_DATABASE_NAME: 914 case SQL_DBMS_NAME: 915 case SQL_DBMS_VER: 916 case SQL_DESCRIBE_PARAMETER: 917 case SQL_DRIVER_NAME: 918 case SQL_DRIVER_ODBC_VER: 919 case SQL_DRIVER_VER: 920 case SQL_ODBC_VER: 921 case SQL_EXPRESSIONS_IN_ORDERBY: 922 case SQL_IDENTIFIER_QUOTE_CHAR: 923 case SQL_INTEGRITY: 924 case SQL_KEYWORDS: 925 case SQL_LIKE_ESCAPE_CLAUSE: 926 case SQL_MAX_ROW_SIZE_INCLUDES_LONG: 927 case SQL_MULT_RESULT_SETS: 928 case SQL_MULTIPLE_ACTIVE_TXN: 929 case SQL_NEED_LONG_DATA_LEN: 930 case SQL_ORDER_BY_COLUMNS_IN_SELECT: 931 case SQL_PROCEDURE_TERM: 932 case SQL_PROCEDURES: 933 case SQL_ROW_UPDATES: 934 case SQL_SCHEMA_TERM: 935 case SQL_SEARCH_PATTERN_ESCAPE: 936 case SQL_SERVER_NAME: 937 case SQL_SPECIAL_CHARACTERS: 938 case SQL_TABLE_TERM: 939 case SQL_USER_NAME: 940 case SQL_XOPEN_CLI_YEAR: 941 case SQL_OUTER_JOINS: 942 if (waMode != 'W') 943 { 944 /* ansi=>unicode*/ 945 if ((_InfoValue = malloc(cbInfoValueMax * sizeof(wchar_t) + 1)) == NULL) 946 { 947 PUSHSQLERR (pdbc->herr, en_HY001); 948 return SQL_ERROR; 949 } 950 cbInfoValueMax *= sizeof(wchar_t); 951 } 952 else 953 { 954 /* unicode=>ansi*/ 955 if ((_InfoValue = malloc(cbInfoValueMax + 1)) == NULL) 956 { 957 PUSHSQLERR (pdbc->herr, en_HY001); 958 return SQL_ERROR; 959 } 960 cbInfoValueMax /= sizeof(wchar_t); 961 } 962 infoValueOut = _InfoValue; 963 break; 964 } 965 } 966 967 CALL_UDRIVER(hdbc, pdbc, retcode, hproc, penv->unicode_driver, 968 en_GetInfo, (pdbc->dhdbc, fInfoType, infoValueOut, cbInfoValueMax, 969 pcbInfoValue)); 970 971 if (hproc == SQL_NULL_HPROC) 972 { 973 PUSHSQLERR (pdbc->herr, en_IM001); 974 return SQL_ERROR; 975 } 976 977 if (retcode == SQL_ERROR && fInfoType == SQL_DRIVER_ODBC_VER) 978 { 979 if (waMode != 'W') 980 { 981 STRCPY (buf, "01.00"); 982 983 if (rgbInfoValue != NULL && cbInfoValueMax > 0) 984 { 985 len = STRLEN (buf); 986 987 if (len > cbInfoValueMax - 1) 988 { 989 len = cbInfoValueMax - 1; 990 ret = -1; 991 } 992 else 993 { 994 ret = 0; 995 } 996 997 STRNCPY (rgbInfoValue, buf, len); 998 ((char *) rgbInfoValue)[len] = '\0'; 999 } 1000 1001 if (pcbInfoValue != NULL) 1002 *pcbInfoValue = (SWORD) len; 1003 } 1004 else 1005 { 1006 ret = dm_StrCopyOut2_A2W ((SQLCHAR *) "01.00", 1007 (SQLWCHAR *) rgbInfoValue, 1008 cbInfoValueMax / sizeof(wchar_t), pcbInfoValue); 1009 if (pcbInfoValue) 1010 *pcbInfoValue = *pcbInfoValue * sizeof(wchar_t); 1011 } 1012 1013 if (ret == -1) 1014 { 1015 PUSHSQLERR (pdbc->herr, en_01004); 1016 retcode = SQL_SUCCESS_WITH_INFO; 1017 } 1018 else 1019 { 1020 retcode = SQL_SUCCESS; 1021 } 1022 1023 } 1024 else if (rgbInfoValue 1025 && SQL_SUCCEEDED (retcode) 1026 && ((penv->unicode_driver && waMode != 'W') 1027 || (!penv->unicode_driver && waMode == 'W'))) 1028 { 1029 switch(fInfoType) 1030 { 1031 case SQL_ACCESSIBLE_PROCEDURES: 1032 case SQL_ACCESSIBLE_TABLES: 1033 case SQL_CATALOG_NAME: 1034 case SQL_CATALOG_NAME_SEPARATOR: 1035 case SQL_CATALOG_TERM: 1036 case SQL_COLLATION_SEQ: 1037 case SQL_COLUMN_ALIAS: 1038 case SQL_DATA_SOURCE_NAME: 1039 case SQL_DATA_SOURCE_READ_ONLY: 1040 case SQL_DATABASE_NAME: 1041 case SQL_DBMS_NAME: 1042 case SQL_DBMS_VER: 1043 case SQL_DESCRIBE_PARAMETER: 1044 case SQL_DRIVER_NAME: 1045 case SQL_DRIVER_ODBC_VER: 1046 case SQL_DRIVER_VER: 1047 case SQL_ODBC_VER: 1048 case SQL_EXPRESSIONS_IN_ORDERBY: 1049 case SQL_IDENTIFIER_QUOTE_CHAR: 1050 case SQL_INTEGRITY: 1051 case SQL_KEYWORDS: 1052 case SQL_LIKE_ESCAPE_CLAUSE: 1053 case SQL_MAX_ROW_SIZE_INCLUDES_LONG: 1054 case SQL_MULT_RESULT_SETS: 1055 case SQL_MULTIPLE_ACTIVE_TXN: 1056 case SQL_NEED_LONG_DATA_LEN: 1057 case SQL_ORDER_BY_COLUMNS_IN_SELECT: 1058 case SQL_PROCEDURE_TERM: 1059 case SQL_PROCEDURES: 1060 case SQL_ROW_UPDATES: 1061 case SQL_SCHEMA_TERM: 1062 case SQL_SEARCH_PATTERN_ESCAPE: 1063 case SQL_SERVER_NAME: 1064 case SQL_SPECIAL_CHARACTERS: 1065 case SQL_TABLE_TERM: 1066 case SQL_USER_NAME: 1067 case SQL_XOPEN_CLI_YEAR: 1068 case SQL_OUTER_JOINS: 1069 if (waMode != 'W') 1070 { 1071 /* ansi<=unicode*/ 1072 ret = dm_StrCopyOut2_W2A ((SQLWCHAR *) infoValueOut, 1073 (SQLCHAR *) rgbInfoValue, 1074 cbInfoValueMax / sizeof(wchar_t), pcbInfoValue); 1075 } 1076 else 1077 { 1078 /* unicode<=ansi*/ 1079 ret = dm_StrCopyOut2_A2W ((SQLCHAR *) infoValueOut, 1080 (SQLWCHAR *) rgbInfoValue, 1081 cbInfoValueMax, pcbInfoValue); 1082 if (pcbInfoValue) 1083 *pcbInfoValue = *pcbInfoValue * sizeof(wchar_t); 1084 } 1085 1086 if (ret == -1) 1087 { 1088 PUSHSQLERR (pdbc->herr, en_01004); 1089 retcode = SQL_SUCCESS_WITH_INFO; 1090 } 1091 break; 1092 } 1093 } 1094 MEM_FREE(_InfoValue); 1095 1096 return retcode; 1097} 1098 1099 1100SQLRETURN SQL_API 1101SQLGetInfo (SQLHDBC hdbc, 1102 SQLUSMALLINT fInfoType, 1103 SQLPOINTER rgbInfoValue, 1104 SQLSMALLINT cbInfoValueMax, 1105 SQLSMALLINT * pcbInfoValue) 1106{ 1107 ENTER_HDBC (hdbc, 0, 1108 trace_SQLGetInfo (TRACE_ENTER, 1109 hdbc, 1110 fInfoType, 1111 rgbInfoValue, cbInfoValueMax, pcbInfoValue)); 1112 1113 retcode = SQLGetInfo_Internal( 1114 hdbc, 1115 fInfoType, 1116 rgbInfoValue, cbInfoValueMax, pcbInfoValue, 1117 'A'); 1118 1119 LEAVE_HDBC (hdbc, 0, 1120 trace_SQLGetInfo (TRACE_LEAVE, 1121 hdbc, 1122 fInfoType, 1123 rgbInfoValue, cbInfoValueMax, pcbInfoValue)); 1124} 1125 1126 1127#if ODBCVER >= 0x0300 1128SQLRETURN SQL_API 1129SQLGetInfoA (SQLHDBC hdbc, 1130 SQLUSMALLINT fInfoType, 1131 SQLPOINTER rgbInfoValue, 1132 SQLSMALLINT cbInfoValueMax, 1133 SQLSMALLINT * pcbInfoValue) 1134{ 1135 ENTER_HDBC (hdbc, 0, 1136 trace_SQLGetInfo (TRACE_ENTER, 1137 hdbc, 1138 fInfoType, 1139 rgbInfoValue, cbInfoValueMax, pcbInfoValue)); 1140 1141 retcode = SQLGetInfo_Internal( 1142 hdbc, 1143 fInfoType, 1144 rgbInfoValue, cbInfoValueMax, pcbInfoValue, 1145 'A'); 1146 1147 LEAVE_HDBC (hdbc, 0, 1148 trace_SQLGetInfo (TRACE_LEAVE, 1149 hdbc, 1150 fInfoType, 1151 rgbInfoValue, cbInfoValueMax, pcbInfoValue)); 1152} 1153 1154 1155SQLRETURN SQL_API 1156SQLGetInfoW ( 1157 SQLHDBC hdbc, 1158 SQLUSMALLINT fInfoType, 1159 SQLPOINTER rgbInfoValue, 1160 SQLSMALLINT cbInfoValueMax, 1161 SQLSMALLINT * pcbInfoValue) 1162{ 1163 ENTER_HDBC (hdbc, 0, 1164 trace_SQLGetInfoW (TRACE_ENTER, 1165 hdbc, 1166 fInfoType, 1167 rgbInfoValue, cbInfoValueMax, pcbInfoValue)); 1168 1169 retcode = SQLGetInfo_Internal ( 1170 hdbc, 1171 fInfoType, 1172 rgbInfoValue, cbInfoValueMax, pcbInfoValue, 1173 'W'); 1174 1175 LEAVE_HDBC (hdbc, 0, 1176 trace_SQLGetInfoW (TRACE_LEAVE, 1177 hdbc, 1178 fInfoType, 1179 rgbInfoValue, cbInfoValueMax, pcbInfoValue)); 1180} 1181#endif 1182 1183 1184static int FunctionNumbers[] = 1185{ 1186 0 1187#define FUNCDEF(A,B,C) ,A 1188#include "henv.ci" 1189#undef FUNCDEF 1190}; 1191 1192#if (ODBCVER >= 0x0300) 1193 1194#define SQL_ODBC3_SET_FUNC_ON(pfExists, uwAPI) \ 1195 *( ((UWORD*) (pfExists)) + ((uwAPI) >> 4) ) |= (1 << ((uwAPI) & 0x000F)) 1196 1197#define SQL_ODBC3_SET_FUNC_OFF(pfExists, uwAPI) \ 1198 *( ((UWORD*) (pfExists)) + ((uwAPI) >> 4) ) &= !(1 << ((uwAPI) & 0x000F)) 1199 1200#endif 1201 1202 1203static SQLRETURN 1204SQLGetFunctions_Internal ( 1205 SQLHDBC hdbc, 1206 SQLUSMALLINT fFunc, 1207 SQLUSMALLINT * pfExists) 1208{ 1209 CONN (pdbc, hdbc); 1210 HPROC hproc; 1211 SQLRETURN retcode; 1212 int i; 1213 UWORD functions2[100]; 1214#if (ODBCVER >= 0x0300) 1215 UWORD functions3[SQL_API_ODBC3_ALL_FUNCTIONS_SIZE]; 1216#endif 1217 1218 if (pdbc->state == en_dbc_allocated 1219 || pdbc->state == en_dbc_needdata) 1220 { 1221 PUSHSQLERR (pdbc->herr, en_S1010); 1222 1223 return SQL_ERROR; 1224 } 1225 1226 if (pfExists == NULL) 1227 { 1228 return SQL_SUCCESS; 1229 } 1230 1231 /* 1232 * These functions are supported by the iODBC driver manager 1233 */ 1234 if (fFunc == SQL_API_SQLDATASOURCES 1235 || fFunc == SQL_API_SQLDRIVERS 1236#if (ODBCVER >= 0x0300) 1237 || fFunc == SQL_API_SQLGETENVATTR 1238 || fFunc == SQL_API_SQLSETENVATTR 1239#endif 1240 ) 1241 { 1242 *pfExists = (UWORD) 1; 1243 return SQL_SUCCESS; 1244 } 1245 1246 /* 1247 * Check if function number is within ODBC version context 1248 */ 1249#if (ODBCVER < 0x0300) 1250 if (fFunc > SQL_EXT_API_LAST) 1251 { 1252 PUSHSQLERR (pdbc->herr, en_S1095); 1253 1254 return SQL_ERROR; 1255 } 1256#endif 1257 1258 /* 1259 * In a ODBC 2.x driver context, the ODBC 3.x API calls are 1260 * mapped by the driver manager. 1261 */ 1262#if (ODBCVER >= 0x0300) 1263 if (((ENV_t *) pdbc->henv)->dodbc_ver == SQL_OV_ODBC2) 1264 { 1265 switch (fFunc) 1266 { 1267 case SQL_API_ALL_FUNCTIONS: 1268 case SQL_API_ODBC3_ALL_FUNCTIONS: 1269 break; 1270 1271 /* Mapped ODBC3 app -> ODBC2 driver functions */ 1272 case SQL_API_SQLALLOCHANDLE: 1273 case SQL_API_SQLFREEHANDLE: 1274 case SQL_API_SQLSETCONNECTATTR: 1275 case SQL_API_SQLGETCONNECTATTR: 1276 case SQL_API_SQLGETSTMTATTR: 1277 case SQL_API_SQLSETSTMTATTR: 1278 case SQL_API_SQLCOLATTRIBUTE: 1279 case SQL_API_SQLENDTRAN: 1280 case SQL_API_SQLBULKOPERATIONS: 1281 case SQL_API_SQLFETCHSCROLL: 1282 case SQL_API_SQLGETDIAGREC: 1283 case SQL_API_SQLGETDIAGFIELD: 1284 *pfExists = SQL_TRUE; 1285 return SQL_SUCCESS; 1286 1287 case SQL_API_SQLBINDPARAM: 1288 fFunc = SQL_API_SQLBINDPARAMETER; 1289 break; 1290 1291 default: 1292 if (fFunc > SQL_API_SQLBINDPARAMETER) 1293 { 1294 *pfExists = SQL_FALSE; 1295 1296 return SQL_SUCCESS; 1297 } 1298 break; 1299 } 1300 } 1301#endif 1302 1303 1304 /* 1305 * If the driver exports a SQLGetFunctions call, use it 1306 */ 1307 hproc = _iodbcdm_getproc (pdbc, en_GetFunctions); 1308 1309 if (hproc != SQL_NULL_HPROC) 1310 { 1311 CALL_DRIVER (hdbc, pdbc, retcode, hproc, (pdbc->dhdbc, fFunc, pfExists)); 1312 1313 return retcode; 1314 } 1315 1316 /* 1317 * Map deprecated functions 1318 */ 1319 if (fFunc == SQL_API_SQLSETPARAM) 1320 { 1321 fFunc = SQL_API_SQLBINDPARAMETER; 1322 } 1323 1324 /* 1325 * Initialize intermediate result arrays 1326 */ 1327 memset (functions2, '\0', sizeof (functions2)); 1328#if (ODBCVER > 0x0300) 1329 memset (functions3, '\0', sizeof (functions3)); 1330#endif 1331 1332 /* 1333 * Build result array by scanning for all API calls 1334 */ 1335 for (i = 1; i < __LAST_API_FUNCTION__; i++) 1336 { 1337 int j = FunctionNumbers[i]; 1338 1339 hproc = _iodbcdm_getproc (pdbc, i); 1340 1341 if (hproc != SQL_NULL_HPROC) 1342 { 1343 if (j < 100) 1344 functions2[j] = 1; 1345#if (ODBCVER >= 0x0300) 1346 functions3[j >> 4] |= (1 << (j & 0x000F)); 1347#endif 1348 } 1349 } 1350 1351 /* 1352 * Finally return the information 1353 */ 1354 if (fFunc == SQL_API_ALL_FUNCTIONS) 1355 { 1356 memcpy (pfExists, &functions2, sizeof (functions2)); 1357 } 1358#if (ODBCVER < 0x0300) 1359 else 1360 { 1361 *pfExists = functions2[fFunc]; 1362 } 1363#else 1364 else if (fFunc == SQL_API_ODBC3_ALL_FUNCTIONS) 1365 { 1366 memcpy (pfExists, &functions3, sizeof (functions3)); 1367 } 1368 else 1369 { 1370 *pfExists = SQL_FUNC_EXISTS (functions3, fFunc); 1371 } 1372#endif 1373 1374 return SQL_SUCCESS; 1375} 1376 1377 1378SQLRETURN SQL_API 1379SQLGetFunctions ( 1380 SQLHDBC hdbc, 1381 SQLUSMALLINT fFunc, 1382 SQLUSMALLINT * pfExists) 1383{ 1384 ENTER_HDBC (hdbc, 0, 1385 trace_SQLGetFunctions (TRACE_ENTER, 1386 hdbc, 1387 fFunc, 1388 pfExists)); 1389 1390 retcode = SQLGetFunctions_Internal ( 1391 hdbc, 1392 fFunc, 1393 pfExists); 1394 1395 LEAVE_HDBC (hdbc, 0, 1396 trace_SQLGetFunctions (TRACE_LEAVE, 1397 hdbc, 1398 fFunc, 1399 pfExists)); 1400} 1401