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.

2100 lines
64 KiB

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