You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2314 lines
71 KiB

  1. /************ Odbconn C++ Functions Source Code File (.CPP) ************/
  2. /* Name: ODBCONN.CPP Version 1.7 */
  3. /* */
  4. /* (C) Copyright to the author Olivier BERTRAND 1998-2013 */
  5. /* */
  6. /* This file contains the ODBC connection classes functions. */
  7. /***********************************************************************/
  8. /***********************************************************************/
  9. /* Include relevant MariaDB header file. */
  10. /***********************************************************************/
  11. #include "my_global.h"
  12. #if defined(WIN32)
  13. //nclude <io.h>
  14. //nclude <fcntl.h>
  15. #include <direct.h> // for getcwd
  16. #if defined(__BORLANDC__)
  17. #define __MFC_COMPAT__ // To define min/max as macro
  18. #endif
  19. //#include <windows.h>
  20. #else
  21. #if defined(UNIX)
  22. #include <errno.h>
  23. #else
  24. //nclude <io.h>
  25. #endif
  26. //nclude <fcntl.h>
  27. #define NODW
  28. #endif
  29. /***********************************************************************/
  30. /* Required objects includes. */
  31. /***********************************************************************/
  32. #include "global.h"
  33. #include "plgdbsem.h"
  34. #include "xobject.h"
  35. //#include "kindex.h"
  36. #include "xtable.h"
  37. #include "tabodbc.h"
  38. #include "odbccat.h"
  39. #include "plgcnx.h" // For DB types
  40. #include "resource.h"
  41. #include "valblk.h"
  42. #include "osutil.h"
  43. #if defined(WIN32)
  44. /***********************************************************************/
  45. /* For dynamic load of ODBC32.DLL */
  46. /***********************************************************************/
  47. #pragma comment(lib, "odbc32.lib")
  48. extern "C" HINSTANCE s_hModule; // Saved module handle
  49. #endif // WIN32
  50. /***********************************************************************/
  51. /* Some macro's (should be defined elsewhere to be more accessible) */
  52. /***********************************************************************/
  53. #if defined(_DEBUG)
  54. #define ASSERT(f) assert(f)
  55. #define DEBUG_ONLY(f) (f)
  56. #else // !_DEBUG
  57. #define ASSERT(f) ((void)0)
  58. #define DEBUG_ONLY(f) ((void)0)
  59. #endif // !_DEBUG
  60. extern "C" int trace;
  61. /***********************************************************************/
  62. /* GetSQLType: returns the SQL_TYPE corresponding to a PLG type. */
  63. /***********************************************************************/
  64. static short GetSQLType(int type)
  65. {
  66. short tp = SQL_TYPE_NULL;
  67. switch (type) {
  68. case TYPE_STRING: tp = SQL_CHAR; break;
  69. case TYPE_SHORT: tp = SQL_SMALLINT; break;
  70. case TYPE_INT: tp = SQL_INTEGER; break;
  71. case TYPE_DATE: tp = SQL_TIMESTAMP; break;
  72. case TYPE_BIGINT: tp = SQL_BIGINT; break; // (-5)
  73. case TYPE_FLOAT: tp = SQL_DOUBLE; break;
  74. case TYPE_TINY : tp = SQL_TINYINT; break;
  75. } // endswitch type
  76. return tp;
  77. } // end of GetSQLType
  78. /***********************************************************************/
  79. /* GetSQLCType: returns the SQL_C_TYPE corresponding to a PLG type. */
  80. /***********************************************************************/
  81. static int GetSQLCType(int type)
  82. {
  83. int tp = SQL_TYPE_NULL;
  84. switch (type) {
  85. case TYPE_STRING: tp = SQL_C_CHAR; break;
  86. case TYPE_SHORT: tp = SQL_C_SHORT; break;
  87. case TYPE_INT: tp = SQL_C_LONG; break;
  88. case TYPE_DATE: tp = SQL_C_TIMESTAMP; break;
  89. case TYPE_BIGINT: tp = SQL_C_SBIGINT; break;
  90. case TYPE_FLOAT: tp = SQL_C_DOUBLE; break;
  91. case TYPE_TINY : tp = SQL_C_TINYINT; break;
  92. } // endswitch type
  93. return tp;
  94. } // end of GetSQLCType
  95. /***********************************************************************/
  96. /* TranslateSQLType: translate a SQL Type to a PLG type. */
  97. /***********************************************************************/
  98. int TranslateSQLType(int stp, int prec, int& len, char& v)
  99. {
  100. int type;
  101. switch (stp) {
  102. case SQL_VARCHAR: // 12
  103. v = 'V';
  104. case SQL_CHAR: // 1
  105. type = TYPE_STRING;
  106. break;
  107. case SQL_LONGVARCHAR: // (-1)
  108. v = 'V';
  109. type = TYPE_STRING;
  110. len = min(abs(len), 256);
  111. break;
  112. case SQL_NUMERIC: // 2
  113. case SQL_DECIMAL: // 3
  114. type = (prec) ? TYPE_FLOAT
  115. : (len > 10) ? TYPE_BIGINT : TYPE_INT;
  116. break;
  117. case SQL_INTEGER: // 4
  118. type = TYPE_INT;
  119. break;
  120. case SQL_SMALLINT: // 5
  121. type = TYPE_SHORT;
  122. break;
  123. case SQL_TINYINT: // (-6)
  124. case SQL_BIT: // (-7)
  125. type = TYPE_TINY;
  126. break;
  127. case SQL_FLOAT: // 6
  128. case SQL_REAL: // 7
  129. case SQL_DOUBLE: // 8
  130. type = TYPE_FLOAT;
  131. break;
  132. case SQL_DATETIME: // 9
  133. // case SQL_DATE: // 9
  134. type = TYPE_DATE;
  135. len = 10;
  136. break;
  137. case SQL_INTERVAL: // 10
  138. // case SQL_TIME: // 10
  139. type = TYPE_STRING;
  140. len = 8 + ((prec) ? (prec+1) : 0);
  141. break;
  142. case SQL_TIMESTAMP: // 11
  143. type = TYPE_DATE;
  144. len = 19 + ((prec) ? (prec+1) : 0);
  145. break;
  146. case SQL_BIGINT: // (-5)
  147. type = TYPE_BIGINT;
  148. break;
  149. case SQL_UNKNOWN_TYPE: // 0
  150. case SQL_BINARY: // (-2)
  151. case SQL_VARBINARY: // (-3)
  152. case SQL_LONGVARBINARY: // (-4)
  153. // case SQL_BIT: // (-7)
  154. case SQL_GUID: // (-11)
  155. default:
  156. type = TYPE_ERROR;
  157. len = 0;
  158. } // endswitch type
  159. return type;
  160. } // end of TranslateSQLType
  161. #if defined(PROMPT_OK)
  162. /***********************************************************************/
  163. /* ODBCCheckConnection: Check completeness of connection string. */
  164. /***********************************************************************/
  165. char *ODBCCheckConnection(PGLOBAL g, char *dsn, int cop)
  166. {
  167. char *newdsn, dir[_MAX_PATH], buf[_MAX_PATH];
  168. int rc;
  169. DWORD options = ODBConn::openReadOnly;
  170. ODBConn *ocp = new(g) ODBConn(g, NULL);
  171. (void) getcwd(dir, sizeof(dir) - 1);
  172. switch (cop) {
  173. case 1: options |= ODBConn::forceOdbcDialog; break;
  174. case 2: options |= ODBConn::noOdbcDialog; break;
  175. } // endswitch cop
  176. if (ocp->Open(dsn, options) < 1)
  177. newdsn = NULL;
  178. else
  179. newdsn = ocp->GetConnect();
  180. (void) getcwd(buf, sizeof(buf) - 1);
  181. // Some data sources change the current directory
  182. if (strcmp(dir, buf))
  183. rc = chdir(dir);
  184. ocp->Close();
  185. return newdsn; // Return complete connection string
  186. } // end of ODBCCheckConnection
  187. #endif // PROMPT_OK
  188. /***********************************************************************/
  189. /* Allocate the structure used to refer to the result set. */
  190. /***********************************************************************/
  191. static CATPARM *AllocCatInfo(PGLOBAL g, CATINFO fid, char *tab, PQRYRES qrp)
  192. {
  193. size_t i, m, n;
  194. CATPARM *cap;
  195. #if defined(_DEBUG)
  196. assert(qrp);
  197. #endif
  198. m = (size_t)qrp->Maxres;
  199. n = (size_t)qrp->Nbcol;
  200. cap = (CATPARM *)PlugSubAlloc(g, NULL, sizeof(CATPARM));
  201. memset(cap, 0, sizeof(CATPARM));
  202. cap->Id = fid;
  203. cap->Qrp = qrp;
  204. cap->Tab = (PUCHAR)tab;
  205. cap->Vlen = (SQLLEN* *)PlugSubAlloc(g, NULL, n * sizeof(SQLLEN *));
  206. for (i = 0; i < n; i++)
  207. cap->Vlen[i] = (SQLLEN *)PlugSubAlloc(g, NULL, m * sizeof(SQLLEN));
  208. cap->Status = (UWORD *)PlugSubAlloc(g, NULL, m * sizeof(UWORD));
  209. return cap;
  210. } // end of AllocCatInfo
  211. /***********************************************************************/
  212. /* Check for nulls and reset them to Null (?) values. */
  213. /***********************************************************************/
  214. static void ResetNullValues(CATPARM *cap)
  215. {
  216. int i, n, ncol;
  217. PCOLRES crp;
  218. PQRYRES qrp = cap->Qrp;
  219. #if defined(_DEBUG)
  220. assert(qrp);
  221. #endif
  222. ncol = qrp->Nbcol;
  223. for (i = 0, crp = qrp->Colresp; i < ncol && crp; i++, crp = crp->Next)
  224. for (n = 0; n < qrp->Nblin; n++)
  225. if (cap->Vlen[i][n] == SQL_NULL_DATA)
  226. crp->Kdata->Reset(n);
  227. } // end of ResetNullValues
  228. /***********************************************************************/
  229. /* ODBCColumns: constructs the result blocks containing all columns */
  230. /* of an ODBC table that will be retrieved by GetData commands. */
  231. /***********************************************************************/
  232. PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table,
  233. char *colpat, bool info)
  234. {
  235. static int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING,
  236. TYPE_STRING, TYPE_SHORT, TYPE_STRING,
  237. TYPE_INT, TYPE_INT, TYPE_SHORT,
  238. TYPE_SHORT, TYPE_SHORT, TYPE_STRING};
  239. static XFLD fldtyp[] = {FLD_QUALIF, FLD_OWNER, FLD_TABNAME,
  240. FLD_NAME, FLD_TYPE, FLD_TYPENAME,
  241. FLD_PREC, FLD_LENGTH, FLD_SCALE,
  242. FLD_RADIX, FLD_NULL, FLD_REM};
  243. static unsigned int length[] = {0, 0, 0, 0, 6, 20, 10, 10, 6, 6, 6, 128};
  244. int n, ncol = 12;
  245. int maxres;
  246. PQRYRES qrp;
  247. CATPARM *cap;
  248. ODBConn *ocp = NULL;
  249. /************************************************************************/
  250. /* Do an evaluation of the result size. */
  251. /************************************************************************/
  252. if (!info) {
  253. ocp = new(g) ODBConn(g, NULL);
  254. if (ocp->Open(dsn, 10) < 1) // openReadOnly + noODBCdialog
  255. return NULL;
  256. // We fix a MySQL limit because some data sources return 32767
  257. n = ocp->GetMaxValue(SQL_MAX_COLUMNS_IN_TABLE);
  258. maxres = (n) ? min(n, 4096) : 4096;
  259. n = ocp->GetMaxValue(SQL_MAX_QUALIFIER_NAME_LEN);
  260. length[0] = (n) ? (n + 1) : 128;
  261. n = ocp->GetMaxValue(SQL_MAX_USER_NAME_LEN);
  262. length[1] = (n) ? (n + 1) : 128;
  263. n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN);
  264. length[2] = (n) ? (n + 1) : 128;
  265. n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN);
  266. length[3] = (n) ? (n + 1) : 128;
  267. } else { // Info table
  268. maxres = 0;
  269. length[0] = 128;
  270. length[1] = 128;
  271. length[2] = 128;
  272. length[3] = 128;
  273. } // endif ocp
  274. if (trace)
  275. htrc("ODBCColumns: max=%d len=%d,%d,%d\n",
  276. maxres, length[0], length[1], length[2], length[3]);
  277. /************************************************************************/
  278. /* Allocate the structures used to refer to the result set. */
  279. /************************************************************************/
  280. qrp = PlgAllocResult(g, ncol, maxres, IDS_COLUMNS,
  281. buftyp, fldtyp, length, false, true);
  282. if (info) // Info table
  283. return qrp;
  284. if (trace)
  285. htrc("Getting col results ncol=%d\n", qrp->Nbcol);
  286. cap = AllocCatInfo(g, CAT_COL, table, qrp);
  287. cap->Pat = (PUCHAR)colpat;
  288. /************************************************************************/
  289. /* Now get the results into blocks. */
  290. /************************************************************************/
  291. if ((n = ocp->GetCatInfo(cap)) >= 0) {
  292. qrp->Nblin = n;
  293. ResetNullValues(cap);
  294. if (trace)
  295. htrc("Columns: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
  296. } else
  297. qrp = NULL;
  298. /* Cleanup */
  299. ocp->Close();
  300. /************************************************************************/
  301. /* Return the result pointer for use by GetData routines. */
  302. /************************************************************************/
  303. return qrp;
  304. } // end of ODBCColumns
  305. /**************************************************************************/
  306. /* ODBCSrcCols: constructs the result blocks containing the */
  307. /* description of all the columns of a Srcdef option. */
  308. /**************************************************************************/
  309. PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src)
  310. {
  311. ODBConn *ocp = new(g) ODBConn(g, NULL);
  312. return ocp->GetMetaData(g, dsn, src);
  313. } // end of ODBCSrcCols
  314. #if 0
  315. /**************************************************************************/
  316. /* MyODBCCols: returns column info as required by ha_connect::pre_create. */
  317. /**************************************************************************/
  318. PQRYRES MyODBCCols(PGLOBAL g, char *dsn, char *tab, bool info)
  319. {
  320. // int i, type, len, prec;
  321. // PCOLRES crp, crpt, crpl, crpp;
  322. PQRYRES qrp;
  323. ODBConn *ocp;
  324. /**********************************************************************/
  325. /* Open the connection with the ODBC data source. */
  326. /**********************************************************************/
  327. if (!info) {
  328. ocp = new(g) ODBConn(g, NULL);
  329. if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly
  330. return NULL;
  331. } else
  332. ocp = NULL;
  333. /**********************************************************************/
  334. /* Get the information about the ODBC table columns. */
  335. /**********************************************************************/
  336. if ((qrp = ODBCColumns(g, ocp, dsn, tab, NULL)) && ocp)
  337. dsn = ocp->GetConnect(); // Complete connect string
  338. /************************************************************************/
  339. /* Close the local connection. */
  340. /************************************************************************/
  341. if (ocp)
  342. ocp->Close();
  343. if (!qrp)
  344. return NULL; // Error in ODBCColumns
  345. /************************************************************************/
  346. /* Keep only the info used by ha_connect::pre_create. */
  347. /************************************************************************/
  348. qrp->Colresp = qrp->Colresp->Next->Next; // Skip Owner and Table names
  349. crpt = qrp->Colresp->Next; // SQL type
  350. crpl = crpt->Next->Next; // Length
  351. crpp = crpl->Next->Next; // Decimals
  352. for (int i = 0; i < qrp->Nblin; i++) {
  353. // Types must be PLG types, not SQL types
  354. type = crpt->Kdata->GetIntValue(i);
  355. len = crpl->Kdata->GetIntValue(i);
  356. prec = crpp->Kdata->GetIntValue(i);
  357. type = TranslateSQLType(type, prec, len);
  358. crpt->Kdata->SetValue(type, i);
  359. // Some data sources do not count prec in length
  360. if (type == TYPE_FLOAT)
  361. len += (prec + 2); // To be safe
  362. // Could have been changed for blobs or numeric
  363. crpl->Kdata->SetValue(len, i);
  364. } // endfor i
  365. crpp->Next = crpp->Next->Next->Next; // Should be Remark
  366. // Renumber crp's for flag comparison
  367. for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next)
  368. crp->Ncol = ++i;
  369. qrp->Nbcol = i; // Should be 7; was 11, skipped 4
  370. return qrp;
  371. } // end of MyODBCCols
  372. #endif // 0
  373. /*************************************************************************/
  374. /* ODBCDataSources: constructs the result blocks containing all ODBC */
  375. /* data sources available on the local host. */
  376. /* Called with info=true to have result column names. */
  377. /*************************************************************************/
  378. PQRYRES ODBCDataSources(PGLOBAL g, bool info)
  379. {
  380. static int buftyp[] = {TYPE_STRING, TYPE_STRING};
  381. static XFLD fldtyp[] = {FLD_NAME, FLD_REM};
  382. static unsigned int length[] = {0, 256};
  383. int n = 0, ncol = 2;
  384. int maxres;
  385. PQRYRES qrp;
  386. ODBConn *ocp = NULL;
  387. /************************************************************************/
  388. /* Do an evaluation of the result size. */
  389. /************************************************************************/
  390. if (!info) {
  391. ocp = new(g) ODBConn(g, NULL);
  392. n = ocp->GetMaxValue(SQL_MAX_DSN_LENGTH);
  393. length[0] = (n) ? (n + 1) : 256;
  394. maxres = 512; // Estimated max number of data sources
  395. } else {
  396. length[0] = 256;
  397. maxres = 0;
  398. } // endif info
  399. if (trace)
  400. htrc("ODBCDataSources: max=%d len=%d\n", maxres, length[0]);
  401. /************************************************************************/
  402. /* Allocate the structures used to refer to the result set. */
  403. /************************************************************************/
  404. qrp = PlgAllocResult(g, ncol, maxres, IDS_DSRC,
  405. buftyp, fldtyp, length, false, true);
  406. /************************************************************************/
  407. /* Now get the results into blocks. */
  408. /************************************************************************/
  409. if (!info && ocp->GetDataSources(qrp))
  410. qrp = NULL;
  411. /************************************************************************/
  412. /* Return the result pointer for use by GetData routines. */
  413. /************************************************************************/
  414. return qrp;
  415. } // end of ODBCDataSources
  416. /*************************************************************************/
  417. /* ODBCDrivers: constructs the result blocks containing all ODBC */
  418. /* drivers available on the local host. */
  419. /* Called with info=true to have result column names. */
  420. /*************************************************************************/
  421. PQRYRES ODBCDrivers(PGLOBAL g, bool info)
  422. {
  423. static int buftyp[] = {TYPE_STRING, TYPE_STRING};
  424. static XFLD fldtyp[] = {FLD_NAME, FLD_REM};
  425. static unsigned int length[] = {128, 256};
  426. int ncol = 2;
  427. int maxres;
  428. PQRYRES qrp;
  429. ODBConn *ocp = NULL;
  430. /************************************************************************/
  431. /* Do an evaluation of the result size. */
  432. /************************************************************************/
  433. if (!info) {
  434. ocp = new(g) ODBConn(g, NULL);
  435. maxres = 256; // Estimated max number of drivers
  436. } else
  437. maxres = 0;
  438. if (trace)
  439. htrc("ODBCDrivers: max=%d len=%d\n", maxres, length[0]);
  440. /************************************************************************/
  441. /* Allocate the structures used to refer to the result set. */
  442. /************************************************************************/
  443. qrp = PlgAllocResult(g, ncol, maxres, IDS_DRIVER,
  444. buftyp, fldtyp, length, false, true);
  445. /************************************************************************/
  446. /* Now get the results into blocks. */
  447. /************************************************************************/
  448. if (!info && ocp->GetDrivers(qrp))
  449. qrp = NULL;
  450. /************************************************************************/
  451. /* Return the result pointer for use by GetData routines. */
  452. /************************************************************************/
  453. return qrp;
  454. } // end of ODBCDrivers
  455. /***********************************************************************/
  456. /* ODBCTables: constructs the result blocks containing all tables in */
  457. /* an ODBC database that will be retrieved by GetData commands. */
  458. /* Note: The first two columns (Qualifier, Owner) are ignored. */
  459. /***********************************************************************/
  460. PQRYRES ODBCTables(PGLOBAL g, char *dsn, char *tabpat, bool info)
  461. {
  462. static int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING,
  463. TYPE_STRING, TYPE_STRING};
  464. static XFLD fldtyp[] = {FLD_QUALIF, FLD_OWNER, FLD_NAME,
  465. FLD_TYPE, FLD_REM};
  466. static unsigned int length[] = {0, 0, 0, 16, 128};
  467. int n, ncol = 5;
  468. int maxres;
  469. PQRYRES qrp;
  470. CATPARM *cap;
  471. ODBConn *ocp = NULL;
  472. /************************************************************************/
  473. /* Do an evaluation of the result size. */
  474. /************************************************************************/
  475. if (!info) {
  476. /**********************************************************************/
  477. /* Open the connection with the ODBC data source. */
  478. /**********************************************************************/
  479. ocp = new(g) ODBConn(g, NULL);
  480. if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly
  481. return NULL;
  482. maxres = 16384; // This is completely arbitrary
  483. n = ocp->GetMaxValue(SQL_MAX_QUALIFIER_NAME_LEN);
  484. length[0] = (n) ? (n + 1) : 128;
  485. n = ocp->GetMaxValue(SQL_MAX_USER_NAME_LEN);
  486. length[1] = (n) ? (n + 1) : 128;
  487. n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN);
  488. length[2] = (n) ? (n + 1) : 128;
  489. } else {
  490. maxres = 0;
  491. length[0] = 128;
  492. length[1] = 128;
  493. length[2] = 128;
  494. } // endif info
  495. if (trace)
  496. htrc("ODBCTables: max=%d len=%d,%d\n", maxres, length[0], length[1]);
  497. /************************************************************************/
  498. /* Allocate the structures used to refer to the result set. */
  499. /************************************************************************/
  500. qrp = PlgAllocResult(g, ncol, maxres, IDS_TABLES, buftyp,
  501. fldtyp, length, false, true);
  502. if (info)
  503. return qrp;
  504. cap = AllocCatInfo(g, CAT_TAB, tabpat, qrp);
  505. //cap->Pat = (PUCHAR)tabtyp;
  506. if (trace)
  507. htrc("Getting table results ncol=%d\n", cap->Qrp->Nbcol);
  508. /************************************************************************/
  509. /* Now get the results into blocks. */
  510. /************************************************************************/
  511. if ((n = ocp->GetCatInfo(cap)) >= 0) {
  512. qrp->Nblin = n;
  513. ResetNullValues(cap);
  514. if (trace)
  515. htrc("Tables: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
  516. } else
  517. qrp = NULL;
  518. /************************************************************************/
  519. /* Close any local connection. */
  520. /************************************************************************/
  521. ocp->Close();
  522. /************************************************************************/
  523. /* Return the result pointer for use by GetData routines. */
  524. /************************************************************************/
  525. return qrp;
  526. } // end of ODBCTables
  527. #if 0 // Currently not used by CONNECT
  528. /**************************************************************************/
  529. /* PrimaryKeys: constructs the result blocks containing all the */
  530. /* ODBC catalog information concerning primary keys. */
  531. /**************************************************************************/
  532. PQRYRES ODBCPrimaryKeys(PGLOBAL g, ODBConn *op, char *dsn, char *table)
  533. {
  534. static int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING,
  535. TYPE_STRING, TYPE_SHORT, TYPE_STRING};
  536. static unsigned int length[] = {0, 0, 0, 0, 6, 128};
  537. int n, ncol = 5;
  538. int maxres;
  539. PQRYRES qrp;
  540. CATPARM *cap;
  541. ODBConn *ocp = op;
  542. if (!op) {
  543. /**********************************************************************/
  544. /* Open the connection with the ODBC data source. */
  545. /**********************************************************************/
  546. ocp = new(g) ODBConn(g, NULL);
  547. if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly
  548. return NULL;
  549. } // endif op
  550. /************************************************************************/
  551. /* Do an evaluation of the result size. */
  552. /************************************************************************/
  553. n = ocp->GetMaxValue(SQL_MAX_COLUMNS_IN_TABLE);
  554. maxres = (n) ? (int)n : 250;
  555. n = ocp->GetMaxValue(SQL_MAX_QUALIFIER_NAME_LEN);
  556. length[0] = (n) ? (n + 1) : 128;
  557. n = ocp->GetMaxValue(SQL_MAX_USER_NAME_LEN);
  558. length[1] = (n) ? (n + 1) : 128;
  559. n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN);
  560. length[2] = (n) ? (n + 1) : 128;
  561. n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN);
  562. length[3] = (n) ? (n + 1) : 128;
  563. if (trace)
  564. htrc("ODBCPrimaryKeys: max=%d len=%d,%d,%d\n",
  565. maxres, length[0], length[1], length[2]);
  566. /************************************************************************/
  567. /* Allocate the structure used to refer to the result set. */
  568. /************************************************************************/
  569. qrp = PlgAllocResult(g, ncol, maxres, IDS_PKEY,
  570. buftyp, NULL, length, false, true);
  571. if (trace)
  572. htrc("Getting pkey results ncol=%d\n", qrp->Nbcol);
  573. cap = AllocCatInfo(g, CAT_KEY, table, qrp);
  574. /************************************************************************/
  575. /* Now get the results into blocks. */
  576. /************************************************************************/
  577. if ((n = ocp->GetCatInfo(cap)) >= 0) {
  578. qrp->Nblin = n;
  579. ResetNullValues(cap);
  580. if (trace)
  581. htrc("PrimaryKeys: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
  582. } else
  583. qrp = NULL;
  584. /************************************************************************/
  585. /* Close any local connection. */
  586. /************************************************************************/
  587. if (!op)
  588. ocp->Close();
  589. /************************************************************************/
  590. /* Return the result pointer for use by GetData routines. */
  591. /************************************************************************/
  592. return qrp;
  593. } // end of ODBCPrimaryKeys
  594. /**************************************************************************/
  595. /* Statistics: constructs the result blocks containing statistics */
  596. /* about one or several tables to be retrieved by GetData commands. */
  597. /**************************************************************************/
  598. PQRYRES ODBCStatistics(PGLOBAL g, ODBConn *op, char *dsn, char *pat,
  599. int un, int acc)
  600. {
  601. static int buftyp[] = {TYPE_STRING,
  602. TYPE_STRING, TYPE_STRING, TYPE_SHORT, TYPE_STRING,
  603. TYPE_STRING, TYPE_SHORT, TYPE_SHORT, TYPE_STRING,
  604. TYPE_STRING, TYPE_INT, TYPE_INT, TYPE_STRING};
  605. static unsigned int length[] = {0, 0, 0 ,6 ,0 ,0 ,6 ,6 ,0 ,2 ,10 ,10 ,128};
  606. int n, ncol = 13;
  607. int maxres;
  608. PQRYRES qrp;
  609. CATPARM *cap;
  610. ODBConn *ocp = op;
  611. if (!op) {
  612. /**********************************************************************/
  613. /* Open the connection with the ODBC data source. */
  614. /**********************************************************************/
  615. ocp = new(g) ODBConn(g, NULL);
  616. if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly
  617. return NULL;
  618. } // endif op
  619. /************************************************************************/
  620. /* Do an evaluation of the result size. */
  621. /************************************************************************/
  622. n = 1 + ocp->GetMaxValue(SQL_MAX_COLUMNS_IN_INDEX);
  623. maxres = (n) ? (int)n : 32;
  624. n = ocp->GetMaxValue(SQL_MAX_USER_NAME_LEN);
  625. length[1] = (n) ? (n + 1) : 128;
  626. n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN);
  627. length[2] = length[5] = (n) ? (n + 1) : 128;
  628. n = ocp->GetMaxValue(SQL_MAX_QUALIFIER_NAME_LEN);
  629. length[0] = length[4] = (n) ? (n + 1) : length[2];
  630. n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN);
  631. length[7] = (n) ? (n + 1) : 128;
  632. if (trace)
  633. htrc("SemStatistics: max=%d pat=%s\n", maxres, SVP(pat));
  634. /************************************************************************/
  635. /* Allocate the structure used to refer to the result set. */
  636. /************************************************************************/
  637. qrp = PlgAllocResult(g, ncol, maxres, IDS_STAT,
  638. buftyp, NULL, length, false, true);
  639. if (trace)
  640. htrc("Getting stat results ncol=%d\n", qrp->Nbcol);
  641. cap = AllocCatInfo(g, CAT_STAT, pat, qrp);
  642. cap->Unique = (un < 0) ? SQL_INDEX_UNIQUE : (UWORD)un;
  643. cap->Accuracy = (acc < 0) ? SQL_QUICK : (UWORD)acc;
  644. /************************************************************************/
  645. /* Now get the results into blocks. */
  646. /************************************************************************/
  647. if ((n = ocp->GetCatInfo(cap)) >= 0) {
  648. qrp->Nblin = n;
  649. ResetNullValues(cap);
  650. if (trace)
  651. htrc("Statistics: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
  652. } else
  653. qrp = NULL;
  654. /************************************************************************/
  655. /* Close any local connection. */
  656. /************************************************************************/
  657. if (!op)
  658. ocp->Close();
  659. /************************************************************************/
  660. /* Return the result pointer for use by GetData routines. */
  661. /************************************************************************/
  662. return qrp;
  663. } // end of Statistics
  664. #endif // 0
  665. /***********************************************************************/
  666. /* Implementation of DBX class. */
  667. /***********************************************************************/
  668. DBX::DBX(RETCODE rc, PSZ msg)
  669. {
  670. m_RC = rc;
  671. m_Msg = msg;
  672. for (int i = 0; i < MAX_NUM_OF_MSG; i++)
  673. m_ErrMsg[i] = NULL;
  674. } // end of DBX constructor
  675. /***********************************************************************/
  676. /* This function is called by ThrowDBX. */
  677. /***********************************************************************/
  678. void DBX::BuildErrorMessage(ODBConn* pdb, HSTMT hstmt)
  679. {
  680. if (pdb) {
  681. SWORD len;
  682. RETCODE rc;
  683. UCHAR msg[SQL_MAX_MESSAGE_LENGTH + 1];
  684. UCHAR state[SQL_SQLSTATE_SIZE + 1];
  685. SDWORD native;
  686. PGLOBAL g = pdb->m_G;
  687. rc = SQLError(pdb->m_henv, pdb->m_hdbc, hstmt, state,
  688. &native, msg, SQL_MAX_MESSAGE_LENGTH - 1, &len);
  689. if (rc != SQL_INVALID_HANDLE) {
  690. // Skip non-errors
  691. for (int i = 0; i < MAX_NUM_OF_MSG
  692. && (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO)
  693. && strcmp((char*)state, "00000"); i++) {
  694. m_ErrMsg[i] = (PSZ)PlugSubAlloc(g, NULL, strlen((char*)msg) + 1);
  695. strcpy(m_ErrMsg[i], (char*)msg);
  696. if (trace)
  697. htrc("%s: %s, Native=%d\n", state, msg, native);
  698. rc = SQLError(pdb->m_henv, pdb->m_hdbc, hstmt, state,
  699. &native, msg, SQL_MAX_MESSAGE_LENGTH - 1, &len);
  700. } // endfor i
  701. return;
  702. } else {
  703. snprintf((char*)msg, SQL_MAX_MESSAGE_LENGTH + 1, "%s: %s", m_Msg,
  704. MSG(BAD_HANDLE_VAL));
  705. m_ErrMsg[0] = (PSZ)PlugSubAlloc(g, NULL, strlen((char*)msg) + 1);
  706. strcpy(m_ErrMsg[0], (char*)msg);
  707. if (trace)
  708. htrc("%s: rc=%hd\n", SVP(m_ErrMsg[0]), m_RC);
  709. return;
  710. } // endif rc
  711. } else
  712. m_ErrMsg[0] = "No connexion address provided";
  713. if (trace)
  714. htrc("%s: rc=%hd (%s)\n", SVP(m_Msg), m_RC, SVP(m_ErrMsg[0]));
  715. } // end of BuildErrorMessage
  716. const char *DBX::GetErrorMessage(int i)
  717. {
  718. if (i < 0 || i >= MAX_NUM_OF_MSG)
  719. return "No ODBC error";
  720. else if (m_ErrMsg[i])
  721. return m_ErrMsg[i];
  722. else
  723. return (m_Msg) ? m_Msg : "Unknown error";
  724. } // end of GetErrorMessage
  725. /***********************************************************************/
  726. /* ODBConn construction/destruction. */
  727. /***********************************************************************/
  728. ODBConn::ODBConn(PGLOBAL g, TDBODBC *tdbp)
  729. {
  730. m_G = g;
  731. m_Tdb = tdbp;
  732. m_henv = SQL_NULL_HENV;
  733. m_hdbc = SQL_NULL_HDBC;
  734. //m_Recset = NULL
  735. m_hstmt = SQL_NULL_HSTMT;
  736. m_LoginTimeout = DEFAULT_LOGIN_TIMEOUT;
  737. m_QueryTimeout = DEFAULT_QUERY_TIMEOUT;
  738. m_UpdateOptions = 0;
  739. m_RowsetSize = (DWORD)((tdbp) ? tdbp->Rows : 10);
  740. m_Catver = (tdbp) ? tdbp->Catver : 0;
  741. m_Connect = NULL;
  742. m_Updatable = true;
  743. m_Transact = false;
  744. m_IDQuoteChar[0] = '"';
  745. m_IDQuoteChar[1] = 0;
  746. //*m_ErrMsg = '\0';
  747. } // end of ODBConn
  748. //ODBConn::~ODBConn()
  749. // {
  750. //if (Connected())
  751. // EndCom();
  752. // } // end of ~ODBConn
  753. /***********************************************************************/
  754. /* Screen for errors. */
  755. /***********************************************************************/
  756. bool ODBConn::Check(RETCODE rc)
  757. {
  758. switch (rc) {
  759. case SQL_SUCCESS_WITH_INFO:
  760. if (trace) {
  761. DBX x(rc);
  762. x.BuildErrorMessage(this, m_hstmt);
  763. htrc("ODBC Success With Info, hstmt=%p %s\n",
  764. m_hstmt, x.GetErrorMessage(0));
  765. } // endif trace
  766. // Fall through
  767. case SQL_SUCCESS:
  768. case SQL_NO_DATA_FOUND:
  769. return true;
  770. } // endswitch rc
  771. return false;
  772. } // end of Check
  773. /***********************************************************************/
  774. /* DB exception throw routines. */
  775. /***********************************************************************/
  776. void ODBConn::ThrowDBX(RETCODE rc, PSZ msg, HSTMT hstmt)
  777. {
  778. DBX* xp = new(m_G) DBX(rc, msg);
  779. xp->BuildErrorMessage(this, hstmt);
  780. throw xp;
  781. } // end of ThrowDBX
  782. void ODBConn::ThrowDBX(PSZ msg)
  783. {
  784. DBX* xp = new(m_G) DBX(0, msg);
  785. xp->m_ErrMsg[0] = msg;
  786. throw xp;
  787. } // end of ThrowDBX
  788. /***********************************************************************/
  789. /* Utility routine. */
  790. /***********************************************************************/
  791. PSZ ODBConn::GetStringInfo(ushort infotype)
  792. {
  793. //ASSERT(m_hdbc != SQL_NULL_HDBC);
  794. char *p, buffer[MAX_STRING_INFO];
  795. SWORD result;
  796. RETCODE rc;
  797. rc = SQLGetInfo(m_hdbc, infotype, buffer, sizeof(buffer), &result);
  798. if (!Check(rc)) {
  799. ThrowDBX(rc, "SQLGetInfo"); // Temporary
  800. // *buffer = '\0';
  801. } // endif rc
  802. p = (char *)PlugSubAlloc(m_G, NULL, strlen(buffer) + 1);
  803. strcpy(p, buffer);
  804. return p;
  805. } // end of GetStringInfo
  806. /***********************************************************************/
  807. /* Utility routine. */
  808. /***********************************************************************/
  809. int ODBConn::GetMaxValue(ushort infotype)
  810. {
  811. //ASSERT(m_hdbc != SQL_NULL_HDBC);
  812. ushort maxval;
  813. RETCODE rc;
  814. rc = SQLGetInfo(m_hdbc, infotype, &maxval, 0, NULL);
  815. if (!Check(rc))
  816. maxval = 0;
  817. return (int)maxval;
  818. } // end of GetMaxValue
  819. /***********************************************************************/
  820. /* Utility routines. */
  821. /***********************************************************************/
  822. void ODBConn::OnSetOptions(HSTMT hstmt)
  823. {
  824. RETCODE rc;
  825. ASSERT(m_hdbc != SQL_NULL_HDBC);
  826. if ((signed)m_QueryTimeout != -1) {
  827. // Attempt to set query timeout. Ignore failure
  828. rc = SQLSetStmtOption(hstmt, SQL_QUERY_TIMEOUT, m_QueryTimeout);
  829. if (!Check(rc))
  830. // don't attempt it again
  831. m_QueryTimeout = (DWORD)-1;
  832. } // endif m_QueryTimeout
  833. if (m_RowsetSize > 0) {
  834. // Attempt to set rowset size.
  835. // In case of failure reset it to 0 to use Fetch.
  836. rc = SQLSetStmtOption(hstmt, SQL_ROWSET_SIZE, m_RowsetSize);
  837. if (!Check(rc))
  838. // don't attempt it again
  839. m_RowsetSize = 0;
  840. } // endif m_RowsetSize
  841. } // end of OnSetOptions
  842. /***********************************************************************/
  843. /* Open: connect to a data source. */
  844. /***********************************************************************/
  845. int ODBConn::Open(PSZ ConnectString, DWORD options)
  846. {
  847. PGLOBAL& g = m_G;
  848. //ASSERT_VALID(this);
  849. //ASSERT(ConnectString == NULL || AfxIsValidString(ConnectString));
  850. ASSERT(!(options & noOdbcDialog && options & forceOdbcDialog));
  851. m_Updatable = !(options & openReadOnly);
  852. m_Connect = ConnectString;
  853. // Allocate the HDBC and make connection
  854. try {
  855. /*PSZ ver;*/
  856. AllocConnect(options);
  857. /*ver = GetStringInfo(SQL_ODBC_VER);*/
  858. if (Connect(options)) {
  859. strcpy(g->Message, MSG(CONNECT_CANCEL));
  860. return 0;
  861. } // endif
  862. /*ver = GetStringInfo(SQL_DRIVER_ODBC_VER);*/
  863. } catch(DBX *xp) {
  864. // strcpy(g->Message, xp->m_ErrMsg[0]);
  865. strcpy(g->Message, xp->GetErrorMessage(0));
  866. Close();
  867. // Free();
  868. return -1;
  869. } // end try-catch
  870. // Verify support for required functionality and cache info
  871. VerifyConnect();
  872. GetConnectInfo();
  873. return 1;
  874. } // end of Open
  875. /***********************************************************************/
  876. /* Allocate an henv (first time called) and hdbc. */
  877. /***********************************************************************/
  878. void ODBConn::AllocConnect(DWORD Options)
  879. {
  880. if (m_hdbc != SQL_NULL_HDBC)
  881. return;
  882. RETCODE rc;
  883. //AfxLockGlobals(CRIT_ODBC);
  884. // Need to allocate an environment for first connection
  885. if (m_henv == SQL_NULL_HENV) {
  886. // ASSERT(m_nAlloc == 0);
  887. rc = SQLAllocEnv(&m_henv);
  888. if (!Check(rc)) {
  889. // AfxUnlockGlobals(CRIT_ODBC);
  890. ThrowDBX(rc, "SQLAllocEnv"); // Fatal
  891. } // endif rc
  892. } // endif m_henv
  893. // Do the real thing, allocating connection data
  894. rc = SQLAllocConnect(m_henv, &m_hdbc);
  895. if (!Check(rc)) {
  896. // AfxUnlockGlobals(CRIT_ODBC);
  897. ThrowDBX(rc, "SQLAllocConnect"); // Fatal
  898. } // endif rc
  899. //m_nAlloc++; // allocated at last
  900. //AfxUnlockGlobals(CRIT_ODBC);
  901. #if defined(_DEBUG)
  902. if (Options & traceSQL) {
  903. SQLSetConnectOption(m_hdbc, SQL_OPT_TRACEFILE, (DWORD)"xodbc.out");
  904. SQLSetConnectOption(m_hdbc, SQL_OPT_TRACE, 1);
  905. } // endif
  906. #endif // _DEBUG
  907. rc = SQLSetConnectOption(m_hdbc, SQL_LOGIN_TIMEOUT, m_LoginTimeout);
  908. if (trace && rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
  909. htrc("Warning: Failure setting login timeout\n");
  910. if (!m_Updatable) {
  911. rc = SQLSetConnectOption(m_hdbc, SQL_ACCESS_MODE, SQL_MODE_READ_ONLY);
  912. if (trace && rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
  913. htrc("Warning: Failure setting read only access mode\n");
  914. } // endif
  915. // Turn on cursor lib support
  916. if (Options & useCursorLib)
  917. rc = SQLSetConnectOption(m_hdbc, SQL_ODBC_CURSORS, SQL_CUR_USE_ODBC);
  918. return;
  919. } // end of AllocConnect
  920. /***********************************************************************/
  921. /* Connect to data source using SQLDriverConnect. */
  922. /***********************************************************************/
  923. bool ODBConn::Connect(DWORD Options)
  924. {
  925. RETCODE rc;
  926. SWORD nResult;
  927. PUCHAR ConnOut = (PUCHAR)PlugSubAlloc(m_G, NULL, MAX_CONNECT_LEN);
  928. UWORD wConnectOption = SQL_DRIVER_COMPLETE;
  929. #if defined(WIN32)
  930. HWND hWndTop = GetForegroundWindow();
  931. HWND hWnd = GetParent(hWndTop);
  932. if (hWnd == NULL)
  933. hWnd = GetDesktopWindow();
  934. #else // !WIN32
  935. HWND hWnd = (HWND)1;
  936. #endif // !WIN32
  937. PGLOBAL& g = m_G;
  938. PDBUSER dup = PlgGetUser(g);
  939. //if (Options & noOdbcDialog || dup->Remote)
  940. wConnectOption = SQL_DRIVER_NOPROMPT;
  941. //else if (Options & forceOdbcDialog)
  942. // wConnectOption = SQL_DRIVER_PROMPT;
  943. rc = SQLDriverConnect(m_hdbc, hWnd, (PUCHAR)m_Connect,
  944. SQL_NTS, ConnOut, MAX_CONNECT_LEN,
  945. &nResult, wConnectOption);
  946. #if defined(WIN32)
  947. if (hWndTop)
  948. EnableWindow(hWndTop, true);
  949. #endif // WIN32
  950. // If user hit 'Cancel'
  951. if (rc == SQL_NO_DATA_FOUND) {
  952. Close();
  953. // Free();
  954. return true;
  955. } // endif rc
  956. if (!Check(rc))
  957. ThrowDBX(rc, "SQLDriverConnect");
  958. // Save connect string returned from ODBC
  959. m_Connect = (PSZ)ConnOut;
  960. // All done
  961. return false;
  962. } // end of Connect
  963. void ODBConn::VerifyConnect()
  964. {
  965. #if defined(NEWMSG) || defined(XMSG)
  966. PGLOBAL& g = m_G;
  967. #endif // NEWMSG || XMSG
  968. RETCODE rc;
  969. SWORD result;
  970. SWORD conformance;
  971. rc = SQLGetInfo(m_hdbc, SQL_ODBC_API_CONFORMANCE,
  972. &conformance, sizeof(conformance), &result);
  973. if (!Check(rc))
  974. ThrowDBX(rc, "SQLGetInfo");
  975. if (conformance < SQL_OAC_LEVEL1)
  976. ThrowDBX(MSG(API_CONF_ERROR));
  977. rc = SQLGetInfo(m_hdbc, SQL_ODBC_SQL_CONFORMANCE,
  978. &conformance, sizeof(conformance), &result);
  979. if (!Check(rc))
  980. ThrowDBX(rc, "SQLGetInfo");
  981. if (conformance < SQL_OSC_MINIMUM)
  982. ThrowDBX(MSG(SQL_CONF_ERROR));
  983. } // end of VerifyConnect
  984. void ODBConn::GetConnectInfo()
  985. {
  986. RETCODE rc;
  987. SWORD nResult;
  988. #if 0 // Update not implemented yet
  989. UDWORD DrvPosOp;
  990. // Reset the database update options
  991. m_UpdateOptions = 0;
  992. // Check for SQLSetPos support
  993. rc = SQLGetInfo(m_hdbc, SQL_POS_OPERATIONS,
  994. &DrvPosOp, sizeof(DrvPosOp), &nResult);
  995. if (Check(rc) &&
  996. (DrvPosOp & SQL_POS_UPDATE) &&
  997. (DrvPosOp & SQL_POS_DELETE) &&
  998. (DrvPosOp & SQL_POS_ADD))
  999. m_UpdateOptions = SQL_SETPOSUPDATES;
  1000. // Check for positioned update SQL support
  1001. UDWORD PosStatements;
  1002. rc = SQLGetInfo(m_hdbc, SQL_POSITIONED_STATEMENTS,
  1003. &PosStatements, sizeof(PosStatements),
  1004. &nResult);
  1005. if (Check(rc) &&
  1006. (PosStatements & SQL_PS_POSITIONED_DELETE) &&
  1007. (PosStatements & SQL_PS_POSITIONED_UPDATE))
  1008. m_UpdateOptions |= SQL_POSITIONEDSQL;
  1009. if (m_Updatable) {
  1010. // Make sure data source is Updatable
  1011. char ReadOnly[10];
  1012. rc = SQLGetInfo(m_hdbc, SQL_DATA_SOURCE_READ_ONLY,
  1013. ReadOnly, sizeof(ReadOnly), &nResult);
  1014. if (Check(rc) && nResult == 1)
  1015. m_Updatable = !!strcmp(ReadOnly, "Y");
  1016. else
  1017. m_Updatable = false;
  1018. if (trace)
  1019. htrc("Warning: data source is readonly\n");
  1020. } else // Make data source is !Updatable
  1021. rc = SQLSetConnectOption(m_hdbc, SQL_ACCESS_MODE,
  1022. SQL_MODE_READ_ONLY);
  1023. #endif // 0
  1024. // Get the quote char to use when constructing SQL
  1025. rc = SQLGetInfo(m_hdbc, SQL_IDENTIFIER_QUOTE_CHAR,
  1026. m_IDQuoteChar, sizeof(m_IDQuoteChar), &nResult);
  1027. if (trace)
  1028. htrc("DBMS: %s, Version: %s, rc=%d\n",
  1029. GetStringInfo(SQL_DBMS_NAME), GetStringInfo(SQL_DBMS_VER), rc);
  1030. } // end of GetConnectInfo
  1031. /***********************************************************************/
  1032. /* Allocate record set and execute an SQL query. */
  1033. /***********************************************************************/
  1034. int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols)
  1035. {
  1036. PGLOBAL& g = m_G;
  1037. void *buffer;
  1038. bool b;
  1039. UWORD n;
  1040. SWORD len, tp, ncol = 0;
  1041. ODBCCOL *colp;
  1042. RETCODE rc;
  1043. HSTMT hstmt;
  1044. try {
  1045. b = false;
  1046. if (m_hstmt) {
  1047. // All this did not seems to make sense and was been commented out
  1048. // if (IsOpen())
  1049. // Close(SQL_CLOSE);
  1050. rc = SQLFreeStmt(m_hstmt, SQL_CLOSE);
  1051. if (trace && !Check(rc))
  1052. htrc("Error: SQLFreeStmt rc=%d\n", rc);
  1053. hstmt = m_hstmt;
  1054. m_hstmt = NULL;
  1055. ThrowDBX(MSG(SEQUENCE_ERROR));
  1056. } else {
  1057. rc = SQLAllocStmt(m_hdbc, &hstmt);
  1058. if (!Check(rc))
  1059. ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
  1060. } // endif hstmt
  1061. OnSetOptions(hstmt);
  1062. b = true;
  1063. if (trace)
  1064. htrc("ExecDirect hstmt=%p %.64s\n", hstmt, sql);
  1065. if (m_Tdb->Srcdef) {
  1066. // Be sure this is a query returning a result set
  1067. do {
  1068. rc = SQLPrepare(hstmt, (PUCHAR)sql, SQL_NTS);
  1069. } while (rc == SQL_STILL_EXECUTING);
  1070. if (!Check(rc))
  1071. ThrowDBX(rc, "SQLPrepare", hstmt);
  1072. if (!Check(rc = SQLNumResultCols(hstmt, &ncol)))
  1073. ThrowDBX(rc, "SQLNumResultCols", hstmt);
  1074. if (ncol == 0) {
  1075. strcpy(g->Message, "This Srcdef does not return a result set");
  1076. return -1;
  1077. } // endif ncol
  1078. // Ok, now we can proceed
  1079. do {
  1080. rc = SQLExecute(hstmt);
  1081. } while (rc == SQL_STILL_EXECUTING);
  1082. if (!Check(rc))
  1083. ThrowDBX(rc, "SQLExecute", hstmt);
  1084. } else {
  1085. do {
  1086. rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS);
  1087. } while (rc == SQL_STILL_EXECUTING);
  1088. if (!Check(rc))
  1089. ThrowDBX(rc, "SQLExecDirect", hstmt);
  1090. do {
  1091. rc = SQLNumResultCols(hstmt, &ncol);
  1092. } while (rc == SQL_STILL_EXECUTING);
  1093. } // endif Srcdef
  1094. for (n = 0, colp = tocols; colp; colp = (PODBCCOL)colp->GetNext())
  1095. if (!colp->IsSpecial())
  1096. n++;
  1097. // n can be 0 for query such as Select count(*) from table
  1098. if (n && n != (UWORD)ncol)
  1099. ThrowDBX(MSG(COL_NUM_MISM));
  1100. // Now bind the column buffers
  1101. for (n = 1, colp = tocols; colp; colp = (PODBCCOL)colp->GetNext())
  1102. if (!colp->IsSpecial()) {
  1103. buffer = colp->GetBuffer(m_RowsetSize);
  1104. len = colp->GetBuflen();
  1105. tp = GetSQLCType(colp->GetResultType());
  1106. if (tp == SQL_TYPE_NULL) {
  1107. sprintf(m_G->Message, MSG(INV_COLUMN_TYPE),
  1108. colp->GetResultType(), SVP(colp->GetName()));
  1109. ThrowDBX(m_G->Message);
  1110. } // endif tp
  1111. if (trace)
  1112. htrc("Binding col=%u type=%d buf=%p len=%d slen=%p\n",
  1113. n, tp, buffer, len, colp->GetStrLen());
  1114. rc = SQLBindCol(hstmt, n, tp, buffer, len, colp->GetStrLen());
  1115. if (!Check(rc))
  1116. ThrowDBX(rc, "SQLBindCol", hstmt);
  1117. n++;
  1118. } // endif pcol
  1119. } catch(DBX *x) {
  1120. if (trace)
  1121. for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
  1122. htrc(x->m_ErrMsg[i]);
  1123. strcpy(m_G->Message, x->GetErrorMessage(0));
  1124. if (b)
  1125. SQLCancel(hstmt);
  1126. rc = SQLFreeStmt(hstmt, SQL_DROP);
  1127. m_hstmt = NULL;
  1128. return -1;
  1129. } // end try/catch
  1130. m_hstmt = hstmt;
  1131. return (int)m_RowsetSize; // May have been reset in OnSetOptions
  1132. } // end of ExecDirectSQL
  1133. /***********************************************************************/
  1134. /* Get the number of lines of the result set. */
  1135. /***********************************************************************/
  1136. int ODBConn::GetResultSize(char *sql, ODBCCOL *colp)
  1137. {
  1138. int n = 0;
  1139. RETCODE rc;
  1140. if (ExecDirectSQL(sql, colp) < 0)
  1141. return -1;
  1142. try {
  1143. for (n = 0; ; n++) {
  1144. do {
  1145. rc = SQLFetch(m_hstmt);
  1146. } while (rc == SQL_STILL_EXECUTING);
  1147. if (!Check(rc))
  1148. ThrowDBX(rc, "SQLFetch", m_hstmt);
  1149. if (rc == SQL_NO_DATA_FOUND)
  1150. break;
  1151. } // endfor n
  1152. } catch(DBX *x) {
  1153. strcpy(m_G->Message, x->GetErrorMessage(0));
  1154. if (trace)
  1155. for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
  1156. htrc(x->m_ErrMsg[i]);
  1157. SQLCancel(m_hstmt);
  1158. n = -2;
  1159. } // end try/catch
  1160. rc = SQLFreeStmt(m_hstmt, SQL_DROP);
  1161. m_hstmt = NULL;
  1162. if (n != 1)
  1163. return -3;
  1164. else
  1165. return colp->GetIntValue();
  1166. } // end of GetResultSize
  1167. /***********************************************************************/
  1168. /* Fetch next row. */
  1169. /***********************************************************************/
  1170. int ODBConn::Fetch()
  1171. {
  1172. ASSERT(m_hstmt);
  1173. int irc;
  1174. SQLULEN crow;
  1175. RETCODE rc;
  1176. PGLOBAL& g = m_G;
  1177. try {
  1178. // do {
  1179. if (m_RowsetSize) {
  1180. rc = SQLExtendedFetch(m_hstmt, SQL_FETCH_NEXT, 1, &crow, NULL);
  1181. } else {
  1182. rc = SQLFetch(m_hstmt);
  1183. crow = 1;
  1184. } // endif m_RowsetSize
  1185. // } while (rc == SQL_STILL_EXECUTING);
  1186. if (trace > 1)
  1187. htrc("Fetch: hstmt=%p RowseSize=%d rc=%d\n",
  1188. m_hstmt, m_RowsetSize, rc);
  1189. if (!Check(rc))
  1190. ThrowDBX(rc, "Fetch", m_hstmt);
  1191. irc = (rc == SQL_NO_DATA_FOUND) ? 0 : (int)crow;
  1192. } catch(DBX *x) {
  1193. if (trace)
  1194. for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
  1195. htrc(x->m_ErrMsg[i]);
  1196. strcpy(g->Message, x->GetErrorMessage(0));
  1197. irc = -1;
  1198. } // end try/catch
  1199. return irc;
  1200. } // end of Fetch
  1201. /***********************************************************************/
  1202. /* Prepare an SQL statement for insert. */
  1203. /***********************************************************************/
  1204. int ODBConn::PrepareSQL(char *sql)
  1205. {
  1206. PGLOBAL& g = m_G;
  1207. bool b;
  1208. UINT txn = 0;
  1209. SWORD nparm;
  1210. RETCODE rc;
  1211. HSTMT hstmt;
  1212. if (m_Tdb->GetMode() != MODE_READ) {
  1213. // Does the data source support transactions
  1214. rc = SQLGetInfo(m_hdbc, SQL_TXN_CAPABLE, &txn, 0, NULL);
  1215. if (Check(rc) && txn != SQL_TC_NONE) try {
  1216. rc = SQLSetConnectAttr(m_hdbc, SQL_ATTR_AUTOCOMMIT,
  1217. SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER);
  1218. if (!Check(rc))
  1219. ThrowDBX(SQL_INVALID_HANDLE, "SQLSetConnectAttr");
  1220. m_Transact = true;
  1221. } catch(DBX *x) {
  1222. if (trace)
  1223. for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
  1224. htrc(x->m_ErrMsg[i]);
  1225. strcpy(g->Message, x->GetErrorMessage(0));
  1226. } // end try/catch
  1227. } // endif Mode
  1228. try {
  1229. b = false;
  1230. if (m_hstmt) {
  1231. RETCODE rc = SQLFreeStmt(m_hstmt, SQL_CLOSE);
  1232. hstmt = m_hstmt;
  1233. m_hstmt = NULL;
  1234. if (m_Tdb->GetAmType() != TYPE_AM_XDBC)
  1235. ThrowDBX(MSG(SEQUENCE_ERROR));
  1236. } // endif m_hstmt
  1237. rc = SQLAllocStmt(m_hdbc, &hstmt);
  1238. if (!Check(rc))
  1239. ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
  1240. OnSetOptions(hstmt);
  1241. b = true;
  1242. if (trace)
  1243. htrc("Prepare hstmt=%p %.64s\n", hstmt, sql);
  1244. do {
  1245. rc = SQLPrepare(hstmt, (PUCHAR)sql, SQL_NTS);
  1246. } while (rc == SQL_STILL_EXECUTING);
  1247. if (!Check(rc))
  1248. ThrowDBX(rc, "SQLPrepare", hstmt);
  1249. do {
  1250. rc = SQLNumParams(hstmt, &nparm);
  1251. } while (rc == SQL_STILL_EXECUTING);
  1252. } catch(DBX *x) {
  1253. if (trace)
  1254. for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
  1255. htrc(x->m_ErrMsg[i]);
  1256. strcpy(g->Message, x->GetErrorMessage(0));
  1257. if (b)
  1258. SQLCancel(hstmt);
  1259. rc = SQLFreeStmt(hstmt, SQL_DROP);
  1260. m_hstmt = NULL;
  1261. if (m_Transact) {
  1262. rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_ROLLBACK);
  1263. m_Transact = false;
  1264. } // endif m_Transact
  1265. return -1;
  1266. } // end try/catch
  1267. m_hstmt = hstmt;
  1268. return (int)nparm;
  1269. } // end of PrepareSQL
  1270. /***********************************************************************/
  1271. /* Execute a prepared statement. */
  1272. /***********************************************************************/
  1273. int ODBConn::ExecuteSQL(void)
  1274. {
  1275. PGLOBAL& g = m_G;
  1276. SWORD ncol = 0;
  1277. RETCODE rc;
  1278. SQLLEN afrw = -1;
  1279. try {
  1280. do {
  1281. rc = SQLExecute(m_hstmt);
  1282. } while (rc == SQL_STILL_EXECUTING);
  1283. if (!Check(rc))
  1284. ThrowDBX(rc, "SQLExecute", m_hstmt);
  1285. if (!Check(rc = SQLNumResultCols(m_hstmt, &ncol)))
  1286. ThrowDBX(rc, "SQLNumResultCols", m_hstmt);
  1287. if (ncol) {
  1288. // This should never happen while inserting
  1289. strcpy(g->Message, "Logical error while inserting");
  1290. } else {
  1291. // Insert, Update or Delete statement
  1292. if (!Check(rc = SQLRowCount(m_hstmt, &afrw)))
  1293. ThrowDBX(rc, "SQLRowCount", m_hstmt);
  1294. } // endif ncol
  1295. } catch(DBX *x) {
  1296. strcpy(m_G->Message, x->GetErrorMessage(0));
  1297. SQLCancel(m_hstmt);
  1298. rc = SQLFreeStmt(m_hstmt, SQL_DROP);
  1299. m_hstmt = NULL;
  1300. if (m_Transact) {
  1301. rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_ROLLBACK);
  1302. m_Transact = false;
  1303. } // endif m_Transact
  1304. afrw = -1;
  1305. } // end try/catch
  1306. return (int)afrw;
  1307. } // end of ExecuteSQL
  1308. /***********************************************************************/
  1309. /* Bind a parameter for inserting. */
  1310. /***********************************************************************/
  1311. bool ODBConn::BindParam(ODBCCOL *colp)
  1312. {
  1313. void *buf;
  1314. UWORD n = colp->GetRank();
  1315. SWORD ct, sqlt;
  1316. UDWORD len;
  1317. SQLLEN *strlen = colp->GetStrLen();
  1318. RETCODE rc;
  1319. #if 0
  1320. try {
  1321. SWORD dec, nul;
  1322. rc = SQLDescribeParam(m_hstmt, n, &sqlt, &len, &dec, &nul);
  1323. if (!Check(rc))
  1324. ThrowDBX(rc, m_hstmt);
  1325. } catch(DBX *x) {
  1326. strcpy(m_G->Message, x->GetErrorMessage(0));
  1327. } // end try/catch
  1328. #endif // 0
  1329. buf = colp->GetBuffer(0);
  1330. len = IsTypeNum(colp->GetResultType()) ? 0 : colp->GetBuflen();
  1331. ct = GetSQLCType(colp->GetResultType());
  1332. sqlt = GetSQLType(colp->GetResultType());
  1333. *strlen = IsTypeNum(colp->GetResultType()) ? 0 : SQL_NTS;
  1334. try {
  1335. rc = SQLBindParameter(m_hstmt, n, SQL_PARAM_INPUT, ct, sqlt,
  1336. len, 0, buf, 0, strlen);
  1337. if (!Check(rc))
  1338. ThrowDBX(rc, "SQLBindParameter", m_hstmt);
  1339. } catch(DBX *x) {
  1340. strcpy(m_G->Message, x->GetErrorMessage(0));
  1341. SQLCancel(m_hstmt);
  1342. rc = SQLFreeStmt(m_hstmt, SQL_DROP);
  1343. m_hstmt = NULL;
  1344. return true;
  1345. } // end try/catch
  1346. return false;
  1347. } // end of BindParam
  1348. /***********************************************************************/
  1349. /* Execute an SQL command. */
  1350. /***********************************************************************/
  1351. bool ODBConn::ExecSQLcommand(char *sql)
  1352. {
  1353. char cmd[16];
  1354. bool b, rcd = false;
  1355. UINT txn = 0;
  1356. PGLOBAL& g = m_G;
  1357. SWORD ncol = 0;
  1358. SQLLEN afrw;
  1359. RETCODE rc;
  1360. HSTMT hstmt;
  1361. try {
  1362. b = FALSE;
  1363. // Check whether we should use transaction
  1364. if (sscanf(sql, " %15s ", cmd) == 1) {
  1365. if (!stricmp(cmd, "INSERT") || !stricmp(cmd, "UPDATE") ||
  1366. !stricmp(cmd, "DELETE") || !stricmp(cmd, "REPLACE")) {
  1367. // Does the data source support transactions
  1368. rc = SQLGetInfo(m_hdbc, SQL_TXN_CAPABLE, &txn, 0, NULL);
  1369. if (Check(rc) && txn != SQL_TC_NONE) {
  1370. rc = SQLSetConnectAttr(m_hdbc, SQL_ATTR_AUTOCOMMIT,
  1371. SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER);
  1372. if (!Check(rc))
  1373. ThrowDBX(SQL_INVALID_HANDLE, "SQLSetConnectAttr");
  1374. m_Transact = TRUE;
  1375. } // endif txn
  1376. } // endif cmd
  1377. } // endif sql
  1378. // Allocate the statement handle
  1379. rc = SQLAllocStmt(m_hdbc, &hstmt);
  1380. if (!Check(rc))
  1381. ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
  1382. OnSetOptions(hstmt);
  1383. b = true;
  1384. if (trace)
  1385. htrc("ExecSQLcommand hstmt=%p %.64s\n", hstmt, sql);
  1386. // Proceed with command execution
  1387. do {
  1388. rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS);
  1389. } while (rc == SQL_STILL_EXECUTING);
  1390. if (!Check(rc))
  1391. ThrowDBX(rc, "SQLExecDirect", hstmt);
  1392. // Check whether this is a query returning a result set
  1393. if (!Check(rc = SQLNumResultCols(hstmt, &ncol)))
  1394. ThrowDBX(rc, "SQLNumResultCols", hstmt);
  1395. if (!ncol) {
  1396. if (!Check(SQLRowCount(hstmt, &afrw)))
  1397. ThrowDBX(rc, "SQLRowCount", hstmt);
  1398. m_Tdb->AftRows = (int)afrw;
  1399. strcpy(g->Message, "Affected rows");
  1400. } else {
  1401. m_Tdb->AftRows = (int)ncol;
  1402. strcpy(g->Message, "Result set column number");
  1403. } // endif ncol
  1404. } catch(DBX *x) {
  1405. if (trace)
  1406. for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
  1407. htrc(x->m_ErrMsg[i]);
  1408. sprintf(g->Message, "Remote: %s", x->GetErrorMessage(0));
  1409. if (b)
  1410. SQLCancel(hstmt);
  1411. m_Tdb->AftRows = -1;
  1412. rcd = true;
  1413. } // end try/catch
  1414. if (!Check(rc = SQLFreeStmt(hstmt, SQL_CLOSE)))
  1415. sprintf(g->Message, "SQLFreeStmt: rc=%d", rc);
  1416. if (m_Transact) {
  1417. // Terminate the transaction
  1418. if (!Check(rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc,
  1419. (rcd) ? SQL_ROLLBACK : SQL_COMMIT)))
  1420. sprintf(g->Message, "SQLEndTran: rc=%d", rc);
  1421. if (!Check(rc = SQLSetConnectAttr(m_hdbc, SQL_ATTR_AUTOCOMMIT,
  1422. (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_UINTEGER)))
  1423. sprintf(g->Message, "SQLSetConnectAttr: rc=%d", rc);
  1424. m_Transact = false;
  1425. } // endif m_Transact
  1426. return rcd;
  1427. } // end of ExecSQLcommand
  1428. /**************************************************************************/
  1429. /* GetMetaData: constructs the result blocks containing the */
  1430. /* description of all the columns of an SQL command. */
  1431. /**************************************************************************/
  1432. PQRYRES ODBConn::GetMetaData(PGLOBAL g, char *dsn, char *src)
  1433. {
  1434. static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_INT,
  1435. TYPE_SHORT, TYPE_SHORT};
  1436. static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_PREC,
  1437. FLD_SCALE, FLD_NULL};
  1438. static unsigned int length[] = {0, 6, 10, 6, 6};
  1439. unsigned char cn[60];
  1440. int qcol = 5;
  1441. short nl, type, prec, nul, cns = (short)sizeof(cn);
  1442. PQRYRES qrp = NULL;
  1443. PCOLRES crp;
  1444. USHORT i;
  1445. SQLULEN n;
  1446. SWORD ncol;
  1447. RETCODE rc;
  1448. HSTMT hstmt;
  1449. if (Open(dsn, 10) < 1) // openReadOnly + noOdbcDialog
  1450. return NULL;
  1451. try {
  1452. rc = SQLAllocStmt(m_hdbc, &hstmt);
  1453. if (!Check(rc))
  1454. ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
  1455. OnSetOptions(hstmt);
  1456. do {
  1457. rc = SQLPrepare(hstmt, (PUCHAR)src, SQL_NTS);
  1458. // rc = SQLExecDirect(hstmt, (PUCHAR)src, SQL_NTS);
  1459. } while (rc == SQL_STILL_EXECUTING);
  1460. if (!Check(rc))
  1461. ThrowDBX(rc, "SQLExecDirect", hstmt);
  1462. do {
  1463. rc = SQLNumResultCols(hstmt, &ncol);
  1464. } while (rc == SQL_STILL_EXECUTING);
  1465. if (!Check(rc))
  1466. ThrowDBX(rc, "SQLNumResultCols", hstmt);
  1467. if (ncol) for (i = 1; i <= ncol; i++) {
  1468. do {
  1469. rc = SQLDescribeCol(hstmt, i, NULL, 0, &nl, NULL, NULL, NULL, NULL);
  1470. } while (rc == SQL_STILL_EXECUTING);
  1471. if (!Check(rc))
  1472. ThrowDBX(rc, "SQLDescribeCol", hstmt);
  1473. length[0] = max(length[0], (UINT)nl);
  1474. } // endfor i
  1475. } catch(DBX *x) {
  1476. strcpy(g->Message, x->GetErrorMessage(0));
  1477. goto err;
  1478. } // end try/catch
  1479. if (!ncol) {
  1480. strcpy(g->Message, "Invalid Srcdef");
  1481. goto err;
  1482. } // endif ncol
  1483. /************************************************************************/
  1484. /* Allocate the structures used to refer to the result set. */
  1485. /************************************************************************/
  1486. qrp = PlgAllocResult(g, qcol, ncol, IDS_COLUMNS + 3,
  1487. buftyp, fldtyp, length, false, true);
  1488. // Some columns must be renamed
  1489. for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next)
  1490. switch (++i) {
  1491. case 3: crp->Name = "Precision"; break;
  1492. case 4: crp->Name = "Scale"; break;
  1493. case 5: crp->Name = "Nullable"; break;
  1494. } // endswitch i
  1495. /************************************************************************/
  1496. /* Now get the results into blocks. */
  1497. /************************************************************************/
  1498. try {
  1499. for (i = 0; i < ncol; i++) {
  1500. do {
  1501. rc = SQLDescribeCol(hstmt, i+1, cn, cns, &nl, &type, &n, &prec, &nul);
  1502. } while (rc == SQL_STILL_EXECUTING);
  1503. if (!Check(rc))
  1504. ThrowDBX(rc, "SQLDescribeCol", hstmt);
  1505. else
  1506. qrp->Nblin++;
  1507. crp = qrp->Colresp; // Column_Name
  1508. crp->Kdata->SetValue((char*)cn, i);
  1509. crp = crp->Next; // Data_Type
  1510. crp->Kdata->SetValue(type, i);
  1511. crp = crp->Next; // Precision (length)
  1512. crp->Kdata->SetValue((int)n, i);
  1513. crp = crp->Next; // Scale
  1514. crp->Kdata->SetValue(prec, i);
  1515. crp = crp->Next; // Nullable
  1516. crp->Kdata->SetValue(nul, i);
  1517. } // endfor i
  1518. } catch(DBX *x) {
  1519. strcpy(g->Message, x->GetErrorMessage(0));
  1520. qrp = NULL;
  1521. } // end try/catch
  1522. /* Cleanup */
  1523. err:
  1524. SQLCancel(hstmt);
  1525. rc = SQLFreeStmt(hstmt, SQL_DROP);
  1526. Close();
  1527. /************************************************************************/
  1528. /* Return the result pointer for use by GetData routines. */
  1529. /************************************************************************/
  1530. return qrp;
  1531. } // end of GetMetaData
  1532. /***********************************************************************/
  1533. /* Get the list of Data Sources and set it in qrp. */
  1534. /***********************************************************************/
  1535. bool ODBConn::GetDataSources(PQRYRES qrp)
  1536. {
  1537. bool rv = false;
  1538. UCHAR *dsn, *des;
  1539. UWORD dir = SQL_FETCH_FIRST;
  1540. SWORD n1, n2, p1, p2;
  1541. PCOLRES crp1 = qrp->Colresp, crp2 = qrp->Colresp->Next;
  1542. RETCODE rc;
  1543. n1 = crp1->Clen;
  1544. n2 = crp2->Clen;
  1545. try {
  1546. rc = SQLAllocEnv(&m_henv);
  1547. if (!Check(rc))
  1548. ThrowDBX(rc, "SQLAllocEnv"); // Fatal
  1549. for (int i = 0; i < qrp->Maxres; i++) {
  1550. dsn = (UCHAR*)crp1->Kdata->GetValPtr(i);
  1551. des = (UCHAR*)crp2->Kdata->GetValPtr(i);
  1552. rc = SQLDataSources(m_henv, dir, dsn, n1, &p1, des, n2, &p2);
  1553. if (rc == SQL_NO_DATA_FOUND)
  1554. break;
  1555. else if (!Check(rc))
  1556. ThrowDBX(rc, "SQLDataSources");
  1557. qrp->Nblin++;
  1558. dir = SQL_FETCH_NEXT;
  1559. } // endfor i
  1560. } catch(DBX *x) {
  1561. strcpy(m_G->Message, x->GetErrorMessage(0));
  1562. rv = true;
  1563. } // end try/catch
  1564. Close();
  1565. return rv;
  1566. } // end of GetDataSources
  1567. /***********************************************************************/
  1568. /* Get the list of Drivers and set it in qrp. */
  1569. /***********************************************************************/
  1570. bool ODBConn::GetDrivers(PQRYRES qrp)
  1571. {
  1572. int i, n;
  1573. bool rv = false;
  1574. UCHAR *des, *att;
  1575. UWORD dir = SQL_FETCH_FIRST;
  1576. SWORD n1, n2, p1, p2;
  1577. PCOLRES crp1 = qrp->Colresp, crp2 = qrp->Colresp->Next;
  1578. RETCODE rc;
  1579. n1 = crp1->Clen;
  1580. n2 = crp2->Clen;
  1581. try {
  1582. rc = SQLAllocEnv(&m_henv);
  1583. if (!Check(rc))
  1584. ThrowDBX(rc, "SQLAllocEnv"); // Fatal
  1585. for (n = 0; n < qrp->Maxres; n++) {
  1586. des = (UCHAR*)crp1->Kdata->GetValPtr(n);
  1587. att = (UCHAR*)crp2->Kdata->GetValPtr(n);
  1588. rc = SQLDrivers(m_henv, dir, des, n1, &p1, att, n2, &p2);
  1589. if (rc == SQL_NO_DATA_FOUND)
  1590. break;
  1591. else if (!Check(rc))
  1592. ThrowDBX(rc, "SQLDrivers");
  1593. // The attributes being separated by '\0', set them to ';'
  1594. for (i = 0; i < p2; i++)
  1595. if (!att[i])
  1596. att[i] = ';';
  1597. qrp->Nblin++;
  1598. dir = SQL_FETCH_NEXT;
  1599. } // endfor n
  1600. } catch(DBX *x) {
  1601. strcpy(m_G->Message, x->GetErrorMessage(0));
  1602. rv = true;
  1603. } // end try/catch
  1604. Close();
  1605. return rv;
  1606. } // end of GetDrivers
  1607. /**
  1608. A helper class to split an optionally qualified table name into components.
  1609. These formats are understood:
  1610. "CatalogName.SchemaName.TableName"
  1611. "SchemaName.TableName"
  1612. "TableName"
  1613. */
  1614. class SQLQualifiedName
  1615. {
  1616. static const uint max_parts= 3; /* Catalog.Schema.Table */
  1617. MYSQL_LEX_STRING m_part[max_parts];
  1618. char m_buf[512];
  1619. void lex_string_set(MYSQL_LEX_STRING *S, char *str, size_t length)
  1620. {
  1621. S->str= str;
  1622. S->length= length;
  1623. }
  1624. void lex_string_shorten_down(MYSQL_LEX_STRING *S, size_t offs)
  1625. {
  1626. DBUG_ASSERT(offs <= S->length);
  1627. S->str+= offs;
  1628. S->length-= offs;
  1629. }
  1630. /*
  1631. Find the rightmost '.' delimiter and return the length
  1632. of the qualifier, including the rightmost '.' delimier.
  1633. For example, for the string {"a.b.c",5} it will return 4,
  1634. which is the length of the qualifier "a.b."
  1635. */
  1636. size_t lex_string_find_qualifier(MYSQL_LEX_STRING *S)
  1637. {
  1638. size_t i;
  1639. for (i= S->length; i > 0; i--)
  1640. {
  1641. if (S->str[i - 1] == '.')
  1642. {
  1643. S->str[i - 1]= '\0';
  1644. return i;
  1645. }
  1646. }
  1647. return 0;
  1648. }
  1649. public:
  1650. /*
  1651. Initialize to the given optionally qualified name.
  1652. NULL pointer in "name" is supported.
  1653. */
  1654. SQLQualifiedName(const char *name)
  1655. {
  1656. size_t len, i= 0;
  1657. if (!name)
  1658. goto ret;
  1659. /* Initialize the first (rightmost) part */
  1660. lex_string_set(&m_part[0], m_buf,
  1661. strmake(m_buf, name, sizeof(m_buf) - 1) - m_buf);
  1662. /* Initialize the other parts, if exist. */
  1663. for (i= 1; i < max_parts; i++)
  1664. {
  1665. if (!(len= lex_string_find_qualifier(&m_part[i - 1])))
  1666. break;
  1667. lex_string_set(&m_part[i], m_part[i - 1].str, len - 1);
  1668. lex_string_shorten_down(&m_part[i - 1], len);
  1669. }
  1670. ret:
  1671. /* Initialize the remaining parts */
  1672. for ( ; i < max_parts; i++)
  1673. lex_string_set(&m_part[i], NULL, 0);
  1674. }
  1675. SQLCHAR *ptr(uint i)
  1676. {
  1677. DBUG_ASSERT(i < max_parts);
  1678. return (SQLCHAR *) (m_part[i].length ? m_part[i].str : NULL);
  1679. }
  1680. size_t length(uint i)
  1681. {
  1682. DBUG_ASSERT(i < max_parts);
  1683. return m_part[i].length;
  1684. }
  1685. };
  1686. /***********************************************************************/
  1687. /* Allocate recset and call SQLTables, SQLColumns or SQLPrimaryKeys. */
  1688. /***********************************************************************/
  1689. int ODBConn::GetCatInfo(CATPARM *cap)
  1690. {
  1691. #if defined(NEWMSG) || defined(XMSG)
  1692. PGLOBAL& g = m_G;
  1693. #endif // NEWMSG || XMSG
  1694. void *buffer;
  1695. int i, irc;
  1696. bool b;
  1697. PSZ fnc = "Unknown";
  1698. UWORD n;
  1699. SWORD ncol, len, tp;
  1700. SQLULEN crow;
  1701. PCOLRES crp;
  1702. RETCODE rc;
  1703. HSTMT hstmt = NULL;
  1704. SQLLEN *vl, *vlen = NULL;
  1705. PVAL *pval = NULL;
  1706. try {
  1707. b = false;
  1708. if (!m_hstmt) {
  1709. rc = SQLAllocStmt(m_hdbc, &hstmt);
  1710. if (!Check(rc))
  1711. ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
  1712. } else
  1713. ThrowDBX(MSG(SEQUENCE_ERROR));
  1714. b = true;
  1715. if ((m_RowsetSize = cap->Qrp->Maxres) > 0) {
  1716. if (m_Catver) {
  1717. // Attempt to set rowset size.
  1718. // In case of failure reset it to 0 to use Fetch.
  1719. if (m_Catver == 3) // ODBC Ver 3
  1720. {
  1721. SQLULEN tmp= m_RowsetSize;
  1722. rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, &tmp, 0);
  1723. }
  1724. else
  1725. rc = SQLSetStmtOption(hstmt, SQL_ROWSET_SIZE, m_RowsetSize);
  1726. if (!Check(rc))
  1727. m_RowsetSize = 1; // don't attempt it again
  1728. // ThrowDBX(rc, hstmt); // Temporary
  1729. if (m_Catver == 3) { // ODBC Ver 3
  1730. rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, cap->Status, 0);
  1731. rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &crow, 0);
  1732. } // endif m_Catver
  1733. } else // ORABUG
  1734. m_RowsetSize = 1;
  1735. } else
  1736. ThrowDBX("0-sized result");
  1737. SQLQualifiedName name((const char *) cap->Tab);
  1738. // Now do call the proper ODBC API
  1739. switch (cap->Id) {
  1740. case CAT_TAB:
  1741. // rc = SQLSetStmtAttr(hstmt, SQL_ATTR_METADATA_ID,
  1742. // (SQLPOINTER)false, 0);
  1743. fnc = "SQLTables";
  1744. rc = SQLTables(hstmt, name.ptr(2), name.length(2),
  1745. name.ptr(1), name.length(1),
  1746. name.ptr(0), name.length(0),
  1747. cap->Pat, SQL_NTS);
  1748. break;
  1749. case CAT_COL:
  1750. // rc = SQLSetStmtAttr(hstmt, SQL_ATTR_METADATA_ID,
  1751. // (SQLPOINTER)true, 0);
  1752. fnc = "SQLColumns";
  1753. rc = SQLColumns(hstmt, name.ptr(2), name.length(2),
  1754. name.ptr(1), name.length(1),
  1755. name.ptr(0), name.length(0),
  1756. cap->Pat, SQL_NTS);
  1757. break;
  1758. case CAT_KEY:
  1759. fnc = "SQLPrimaryKeys";
  1760. rc = SQLPrimaryKeys(hstmt, name.ptr(2), name.length(2),
  1761. name.ptr(1), name.length(1),
  1762. name.ptr(0), name.length(0));
  1763. break;
  1764. case CAT_STAT:
  1765. fnc = "SQLStatistics";
  1766. rc = SQLStatistics(hstmt, name.ptr(2), name.length(2),
  1767. name.ptr(1), name.length(1),
  1768. name.ptr(0), name.length(0),
  1769. cap->Unique, cap->Accuracy);
  1770. break;
  1771. case CAT_SPC:
  1772. ThrowDBX("SQLSpecialColumns not available yet");
  1773. } // endswitch infotype
  1774. if (!Check(rc))
  1775. ThrowDBX(rc, fnc, hstmt);
  1776. rc = SQLNumResultCols(hstmt, &ncol);
  1777. // n because we no more ignore the first column
  1778. if ((n = (UWORD)cap->Qrp->Nbcol) > (UWORD)ncol)
  1779. ThrowDBX(MSG(COL_NUM_MISM));
  1780. if (m_RowsetSize == 1 && cap->Qrp->Maxres > 1) {
  1781. pval = (PVAL *)PlugSubAlloc(m_G, NULL, n * sizeof(PVAL));
  1782. vlen = (SQLLEN *)PlugSubAlloc(m_G, NULL, n * sizeof(SQLLEN *));
  1783. } // endif
  1784. // Now bind the column buffers
  1785. for (n = 0, crp = cap->Qrp->Colresp; crp; crp = crp->Next) {
  1786. if (pval) {
  1787. pval[n] = AllocateValue(m_G, crp->Kdata->GetType(),
  1788. crp->Kdata->GetVlen(), 0);
  1789. buffer = pval[n]->GetTo_Val();
  1790. vl = vlen + n;
  1791. } else {
  1792. buffer = crp->Kdata->GetValPointer();
  1793. vl = cap->Vlen[n];
  1794. } // endif pval
  1795. len = GetTypeSize(crp->Type, crp->Clen);
  1796. tp = GetSQLCType(crp->Type);
  1797. if (tp == SQL_TYPE_NULL) {
  1798. sprintf(m_G->Message, MSG(INV_COLUMN_TYPE), crp->Type, crp->Name);
  1799. ThrowDBX(m_G->Message);
  1800. } // endif tp
  1801. // n + 1 because column numbers begin with 1
  1802. rc = SQLBindCol(hstmt, n + 1, tp, buffer, len, vl);
  1803. if (!Check(rc))
  1804. ThrowDBX(rc, "SQLBindCol", hstmt);
  1805. n++;
  1806. } // endfor crp
  1807. fnc = "SQLFetch";
  1808. // Now fetch the result
  1809. if (m_Catver != 3) {
  1810. if (m_RowsetSize > 1) {
  1811. fnc = "SQLExtendedFetch";
  1812. rc = SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, &crow, cap->Status);
  1813. } else if (pval) {
  1814. for (n = 0; n < cap->Qrp->Maxres; n++) {
  1815. if ((rc = SQLFetch(hstmt)) != SQL_SUCCESS)
  1816. break;
  1817. for (i = 0, crp = cap->Qrp->Colresp; crp; i++, crp = crp->Next) {
  1818. crp->Kdata->SetValue(pval[i], n);
  1819. cap->Vlen[i][n] = vlen[i];
  1820. } // endfor crp
  1821. } // endfor n
  1822. if ((crow = n) && rc == SQL_NO_DATA)
  1823. rc = SQL_SUCCESS;
  1824. } else {
  1825. rc = SQLFetch(hstmt);
  1826. crow = 1;
  1827. } // endif's
  1828. } else // ODBC Ver 3
  1829. rc = SQLFetch(hstmt);
  1830. if (rc == SQL_NO_DATA_FOUND) {
  1831. if (cap->Pat)
  1832. sprintf(m_G->Message, MSG(NO_TABCOL_DATA), cap->Tab, cap->Pat);
  1833. else
  1834. sprintf(m_G->Message, MSG(NO_TAB_DATA), cap->Tab);
  1835. ThrowDBX(m_G->Message);
  1836. } else if (rc != SQL_SUCCESS)
  1837. ThrowDBX(rc, fnc, hstmt);
  1838. irc = (int)crow;
  1839. } catch(DBX *x) {
  1840. if (trace)
  1841. for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
  1842. htrc(x->m_ErrMsg[i]);
  1843. strcpy(m_G->Message, x->GetErrorMessage(0));
  1844. irc = -1;
  1845. } // end try/catch
  1846. if (b)
  1847. SQLCancel(hstmt);
  1848. // All this (hstmt vs> m_hstmt) to be revisited
  1849. if (hstmt)
  1850. rc = SQLFreeStmt(hstmt, SQL_DROP);
  1851. return irc;
  1852. } // end of GetCatInfo
  1853. /***********************************************************************/
  1854. /* Disconnect connection */
  1855. /***********************************************************************/
  1856. void ODBConn::Close()
  1857. {
  1858. RETCODE rc;
  1859. if (m_hstmt) {
  1860. // Is required for multiple tables
  1861. rc = SQLFreeStmt(m_hstmt, SQL_DROP);
  1862. m_hstmt = NULL;
  1863. } // endif m_hstmt
  1864. if (m_hdbc != SQL_NULL_HDBC) {
  1865. if (m_Transact) {
  1866. rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_COMMIT);
  1867. m_Transact = false;
  1868. } // endif m_Transact
  1869. rc = SQLDisconnect(m_hdbc);
  1870. if (trace && rc != SQL_SUCCESS)
  1871. htrc("Error: SQLDisconnect rc=%d\n", rc);
  1872. rc = SQLFreeConnect(m_hdbc);
  1873. if (trace && rc != SQL_SUCCESS)
  1874. htrc("Error: SQLFreeConnect rc=%d\n", rc);
  1875. m_hdbc = SQL_NULL_HDBC;
  1876. } // endif m_hdbc
  1877. if (m_henv != SQL_NULL_HENV) {
  1878. rc = SQLFreeEnv(m_henv);
  1879. if (trace && rc != SQL_SUCCESS) // Nothing we can do
  1880. htrc("Error: SQLFreeEnv failure ignored in Close\n");
  1881. m_henv = SQL_NULL_HENV;
  1882. } // endif m_henv
  1883. } // end of Close