Odb.cpp
上传用户:xaxinn
上传日期:2007-01-03
资源大小:39k
文件大小:18k
源码类别:

Oracle数据库

开发平台:

Visual C++

  1. // Odb.cpp
  2. //
  3. // Release 1, Copyright (C) 1999 Ben Bryant
  4. // This is sample source code, nothing more is implied. Use it only as such.
  5. // This software is provided 'as-is', without warranty. In no event will the
  6. // author be held liable for any damages arising from the use of this software.
  7. // Permission is granted to anyone to use this software for any purpose.
  8. // The origin of this software must not be misrepresented; you must not claim
  9. // that you wrote the original software. Altered source versions must be plainly
  10. // marked as such, and must not be misrepresented as being the original software.
  11. // Ben Bryant bcbryant@firstobject.com
  12. #include "stdafx.h"
  13. #include "Odb.h"
  14. #ifdef _DEBUG
  15. #undef THIS_FILE
  16. static char THIS_FILE[]=__FILE__;
  17. #define new DEBUG_NEW
  18. #endif
  19. //////////////////////////////////////////////////////////////////
  20. // Oracle Call Interface (OCI) Wrapper
  21. //
  22. #ifdef __STDC__
  23. #include <oci.h>
  24. #else
  25. #define __STDC__ 1
  26. #include <oci.h>
  27. #undef __STDC__
  28. #endif
  29. //////////////////////////////////////////////////////////////////////
  30. // COdb::OdbField
  31. //////////////////////////////////////////////////////////////////////
  32. struct COdb::OdbField
  33. {
  34. //
  35. // This structure maintains an OCI field/column/variable
  36. //
  37. CString csName;
  38. dvoid* pBuffer;
  39. ub2 wType;
  40. ub2 wSize;
  41. ub2 wLen;
  42. sb2 nInd;
  43. BOOL bQuotedOnUpdate;
  44. // Methods
  45. OdbField();
  46. ~OdbField();
  47. void SetSize( ub2 w );
  48. void Set( CString cs );
  49. CString Get() const;
  50. void Free();
  51. };
  52. COdb::OdbField::OdbField()
  53. {
  54. pBuffer = NULL;
  55. wType = SQLT_STR;
  56. wSize = 0;
  57. wLen = 0;
  58. nInd = 0;
  59. bQuotedOnUpdate = TRUE;
  60. }
  61. COdb::OdbField::~OdbField()
  62. {
  63. Free();
  64. }
  65. void COdb::OdbField::SetSize( ub2 w )
  66. {
  67. //
  68. // Make sure buffer is at least size w
  69. //
  70. if ( w > wSize || ! pBuffer )
  71. {
  72. Free();
  73. pBuffer = (dvoid*) new char[w];
  74. wSize = w;
  75. }
  76. }
  77. void COdb::OdbField::Set( CString cs )
  78. {
  79. //
  80. // Set buffer to hold CString
  81. //
  82. wLen = cs.GetLength() + 1;
  83. SetSize( wLen );
  84. strcpy( (char*)pBuffer, cs );
  85. }
  86. CString COdb::OdbField::Get() const
  87. {
  88. //
  89. // All fields are converted to strings by the OCI
  90. // because SQLT_STR and SQLT_LNG are specified when binding fields
  91. // Copy from buffer into a string
  92. //
  93. CString csResult;
  94. csResult.Format("%.*s", wLen, (const char*)pBuffer);
  95. csResult.TrimRight();
  96. return csResult;
  97. }
  98. void COdb::OdbField::Free()
  99. {
  100. //
  101. // Free field's allocated buffer
  102. //
  103. if ( pBuffer )
  104. {
  105. delete pBuffer;
  106. pBuffer = NULL;
  107. }
  108. }
  109. //////////////////////////////////////////////////////////////////////
  110. // COdb::OdbRecordSet
  111. //////////////////////////////////////////////////////////////////////
  112. struct COdb::OdbRecordSet
  113. {
  114. //
  115. // This structure maintains a recordset
  116. // A recordset holds a row of select results
  117. //
  118. CPtrArray paFields;
  119. int m_nRows;
  120. CString m_csStatement;
  121. // Methods
  122. OdbRecordSet();
  123. ~OdbRecordSet();
  124. int Find( const CString& csName );
  125. void RemoveAll();
  126. };
  127. COdb::OdbRecordSet::OdbRecordSet()
  128. {
  129. }
  130. COdb::OdbRecordSet::~OdbRecordSet()
  131. {
  132. RemoveAll();
  133. }
  134. void COdb::OdbRecordSet::RemoveAll()
  135. {
  136. //
  137. // Remove fields
  138. //
  139. for ( int iField=0; iField<paFields.GetSize(); ++iField )
  140. delete (OdbField*)paFields[iField];
  141. paFields.RemoveAll();
  142. }
  143. int COdb::OdbRecordSet::Find( const CString& csName )
  144. {
  145. //
  146. // Return iField or -1 if name not found
  147. //
  148. for ( int iField=0; iField<paFields.GetSize(); ++iField )
  149. {
  150. OdbField* pField = (OdbField*)paFields.GetAt( iField );
  151. if ( pField->csName == csName )
  152. return iField;
  153. }
  154. return -1;
  155. }
  156. //////////////////////////////////////////////////////////////////////
  157. // OdbContext
  158. //////////////////////////////////////////////////////////////////////
  159. struct COdb::OdbContext
  160. {
  161. //
  162. // This structure maintains all of the OCI variables
  163. // It is declared here rather than in the header
  164. // to isolate OCI from COdb clients
  165. //
  166. OdbContext();
  167. void Clear();
  168. char szError[512];
  169. HRESULT hr;
  170. OCIEnv* hpEnv;
  171. OCIServer* hpServer;
  172. OCIError* hpErr;
  173. OCISvcCtx* hpContext;
  174. OCIStmt *hpSelect;
  175. OdbRecordSet rsSelect;
  176. };
  177. COdb::OdbContext::OdbContext()
  178. {
  179. Clear();
  180. }
  181. void COdb::OdbContext::Clear()
  182. {
  183. hr = OCI_SUCCESS;
  184. hpEnv = NULL;
  185. hpErr = NULL;
  186. hpServer = NULL;
  187. hpContext = NULL;
  188. hpSelect = NULL;
  189. szError[0] = '';
  190. }
  191. //////////////////////////////////////////////////////////////////////
  192. // COdb
  193. //////////////////////////////////////////////////////////////////////
  194. COdb::COdb()
  195. {
  196. m_po = new OdbContext;
  197. }
  198. COdb::~COdb()
  199. {
  200. Close();
  201. delete m_po;
  202. }
  203. void COdb::CheckErr( short status )
  204. {
  205. //
  206. // This sets the OCI error string
  207. // Later, this might be made to generate an exception
  208. // The table of codes and strings is kept here in a static array
  209. //
  210. static struct CErrorPair
  211. {
  212. sword w;
  213. const char* szText;
  214. }
  215. epaErrors[] =
  216. {
  217. OCI_SUCCESS, "SUCCESS",
  218. OCI_SUCCESS_WITH_INFO, "SUCCESS_WITH_INFO",
  219. OCI_NEED_DATA, "NEED_DATA",
  220. OCI_NO_DATA, "NO_DATA",
  221. OCI_INVALID_HANDLE, "INVALID_HANDLE",
  222. OCI_STILL_EXECUTING, "STILL_EXECUTE",
  223. OCI_CONTINUE, "CONTINUE",
  224. };
  225. // Check status
  226. m_po->hr = status;
  227. if ( m_po->hr == OCI_ERROR )
  228. {
  229. // for the special case OCI_ERROR, call OCIErrorGet for the string
  230. sb4 errcode = 0;
  231.     OCIErrorGet(m_po->hpErr, 1, NULL, &errcode, (unsigned char*)m_po->szError, sizeof(m_po->szError), OCI_HTYPE_ERROR);
  232. }
  233. else
  234. {
  235. // Look for description in static array
  236. DWORD nTotalResultCodes = sizeof(epaErrors) / sizeof(CErrorPair);
  237. for ( int iPos = 0; iPos < sizeof(epaErrors); ++ iPos )
  238. {
  239. if ( epaErrors[iPos].w == m_po->hr )
  240. {
  241. strcpy( m_po->szError, epaErrors[iPos].szText );
  242. break;
  243. }
  244. }
  245. }
  246. }
  247. CString COdb::GetErrorDescription()
  248. {
  249. return m_po->szError;
  250. }
  251. HRESULT COdb::Open( const CString csConnect )
  252. {
  253. //
  254. // This opens a connection with the database
  255. // Connect string format is user/password@host
  256. //
  257. CString csUser, csPassword, csServer;
  258. int iSlash = csConnect.Find("/");
  259. if ( iSlash > -1 )
  260. {
  261. csUser = csConnect.Left(iSlash);
  262. csPassword = csConnect.Mid(iSlash + 1);
  263. int iAt = csConnect.Find("@");
  264. if ( iAt > iSlash )
  265. {
  266. csPassword = csConnect.Mid( iSlash + 1, iAt - iSlash - 1 );
  267. csServer = csConnect.Mid(iAt + 1);
  268. }
  269. }
  270. else
  271. {
  272. csUser = csConnect;
  273. }
  274. if ( csUser.IsEmpty() && csPassword.IsEmpty() )
  275. {
  276. // For default OS logon, specify slash for user name and password
  277. csUser = "/";
  278. csPassword = "/";
  279. }
  280. // First, we make sure its closed
  281. Close();
  282. // Initialize OCI DLL specifying the mode
  283. CheckErr( OCIInitialize( OCI_OBJECT, NULL, NULL, NULL, NULL ) );
  284. if ( FAILED(m_po->hr) )
  285. return m_po->hr;
  286. // Note an alternative is to use the OCILogon/Logoff functions
  287. // Here we allocate all of the handles explicitly
  288. // Initialize the environment handle
  289. CheckErr( OCIEnvInit( &m_po->hpEnv, OCI_DEFAULT, 0, NULL ) );
  290. if ( FAILED(m_po->hr) )
  291. return m_po->hr;
  292. // Allocate error, server, and service context handles
  293. OCIHandleAlloc( m_po->hpEnv, (void**)&m_po->hpErr, OCI_HTYPE_ERROR, 0, NULL );
  294. OCIHandleAlloc( m_po->hpEnv, (void**)&m_po->hpServer, OCI_HTYPE_SERVER, 0, NULL );
  295. OCIHandleAlloc( m_po->hpEnv, (void**)&m_po->hpContext, OCI_HTYPE_SVCCTX, 0, NULL );
  296. // Associate TNS with server handle
  297. CheckErr( OCIServerAttach( m_po->hpServer,
  298. m_po->hpErr,
  299. (const unsigned char*)(const char*)csServer,
  300. csServer.GetLength(),
  301. 0 )
  302. );
  303. if ( FAILED(m_po->hr) )
  304. return m_po->hr;
  305. // Get server version string
  306. const nVersionLength = 1024;
  307. CString csVersion;
  308. CheckErr( OCIServerVersion( m_po->hpServer,
  309. m_po->hpErr,
  310. (text*)csVersion.GetBuffer(nVersionLength),
  311. nVersionLength,
  312. OCI_HTYPE_SERVER )
  313. );
  314. if ( FAILED(m_po->hr) )
  315. return m_po->hr;
  316. csVersion.ReleaseBuffer();
  317. m_csLog += csVersion + "n";
  318. // Specify server handle to service context
  319. CheckErr( OCIAttrSet( m_po->hpContext,
  320. OCI_HTYPE_SVCCTX,
  321. m_po->hpServer,
  322. 0,
  323. OCI_ATTR_SERVER,
  324. m_po->hpErr )
  325. );
  326. if ( FAILED(m_po->hr) )
  327. return m_po->hr;
  328. // Allocate a session handle
  329. OCISession *hpSession = NULL;
  330. OCIHandleAlloc( m_po->hpEnv, (void**)&hpSession, OCI_HTYPE_SESSION, 0, NULL);
  331. // Associate username with session handle
  332. OCIAttrSet( hpSession,
  333. OCI_HTYPE_SESSION,
  334. (void*)(const char*)csUser,
  335. csUser.GetLength(),
  336. OCI_ATTR_USERNAME,
  337. m_po->hpErr
  338. );
  339. // Associate password with session handle
  340. OCIAttrSet( hpSession,
  341. OCI_HTYPE_SESSION,
  342. (void*)(const char*)csPassword,
  343. csPassword.GetLength(),
  344. OCI_ATTR_PASSWORD,
  345. m_po->hpErr
  346. );
  347. // Open session using service context and session handle
  348. CheckErr( OCISessionBegin( m_po->hpContext,
  349. m_po->hpErr,
  350. hpSession,
  351. OCI_CRED_RDBMS,
  352. OCI_DEFAULT )
  353. );
  354. if ( FAILED(m_po->hr) )
  355. return m_po->hr;
  356. // Specify session handle to service context
  357. OCIAttrSet( m_po->hpContext,
  358. OCI_HTYPE_SVCCTX,
  359. hpSession,
  360. 0,
  361. OCI_ATTR_SESSION,
  362. m_po->hpErr
  363. );
  364. // Change date format
  365. if ( SUCCEEDED(m_po->hr) )
  366. {
  367. CString csSQL = "alter session set nls_date_format = 'yyyy-mm-dd hh24:mi:ss'";
  368. m_po->hr = Exec( csSQL );
  369. }
  370. return m_po->hr;
  371. }
  372. HRESULT COdb::Close()
  373. {
  374. //
  375. // This closes and/or cleans up the connection
  376. // It should work even if the connection is closed or partly open
  377. //
  378. m_po->hr = OCI_SUCCESS;
  379. // Free select statement handle
  380. if (m_po->hpSelect)
  381. OCIHandleFree( m_po->hpSelect, OCI_HTYPE_STMT );
  382. // Detach server from server handle
  383. if (m_po->hpErr)
  384. OCIServerDetach( m_po->hpServer, m_po->hpErr, OCI_DEFAULT );
  385. // Free server handle
  386. if (m_po->hpServer)
  387. CheckErr( OCIHandleFree(m_po->hpServer, OCI_HTYPE_SERVER) );
  388. // Free service context
  389. if (m_po->hpContext)
  390. OCIHandleFree( m_po->hpContext, OCI_HTYPE_SVCCTX);
  391. // Free error handle
  392. if (m_po->hpErr)
  393. OCIHandleFree( m_po->hpErr, OCI_HTYPE_ERROR );
  394. m_po->Clear();
  395. return m_po->hr;
  396. }
  397. HRESULT COdb::Exec( const CString csStatement )
  398. {
  399. //
  400. // Execute statement
  401. //
  402. m_csLog += csStatement + "n";
  403. // Allocate statement handle
  404. OCIStmt *hpStatement = NULL;
  405. CheckErr( OCIHandleAlloc( m_po->hpEnv,
  406. (void**)&hpStatement,
  407. OCI_HTYPE_STMT,
  408. 0,
  409. NULL )
  410. );
  411. // Associate statement string with handle
  412. CString csCleanStatement = csStatement;
  413. CleanWhitespace( csCleanStatement );
  414. CheckErr( OCIStmtPrepare( hpStatement,
  415. m_po->hpErr,
  416. (text *)csCleanStatement.GetBuffer(0),
  417. csCleanStatement.GetLength(),
  418. OCI_NTV_SYNTAX,
  419. OCI_DEFAULT )
  420. );
  421. // Execute statement
  422. CheckErr( OCIStmtExecute( m_po->hpContext,
  423. hpStatement,
  424. m_po->hpErr,
  425. 1, // iters
  426. 0, // row offset
  427. NULL, NULL, // snapshot in/out
  428. OCI_DEFAULT )
  429. );
  430. // Free statement handle
  431. if ( hpStatement )
  432. OCIHandleFree( hpStatement, OCI_HTYPE_STMT );
  433. return m_po->hr;
  434. }
  435. HRESULT COdb::Select( const CString csStatement )
  436. {
  437. //
  438. // Execute select statement and parse the select list description
  439. // Use member recordset rsSelect
  440. // See also Get, FetchNext, IsEOS
  441. //
  442. m_csLog += csStatement + "n";
  443. // Free previous select handle and remove fields
  444. m_po->rsSelect.m_nRows = 0;
  445. m_po->rsSelect.RemoveAll();
  446. if (m_po->hpSelect)
  447. OCIHandleFree( m_po->hpSelect, OCI_HTYPE_STMT );
  448. // Allocate statement handle
  449. CheckErr( OCIHandleAlloc( m_po->hpEnv,
  450. (void**)&m_po->hpSelect, // ref to statement handle pointer
  451. OCI_HTYPE_STMT, // type of handle being allocated
  452. 0,
  453. NULL )
  454. );
  455. // Associate statement string with handle
  456. m_po->rsSelect.m_csStatement = csStatement;
  457. CleanWhitespace( m_po->rsSelect.m_csStatement );
  458. CheckErr( OCIStmtPrepare( m_po->hpSelect,
  459. m_po->hpErr, // error handle pointer
  460. (text *)m_po->rsSelect.m_csStatement.GetBuffer(0), // statement string
  461. m_po->rsSelect.m_csStatement.GetLength(),
  462. OCI_NTV_SYNTAX, // specify native syntax
  463. OCI_DEFAULT )
  464. );
  465. // Execute but don't fetch yet
  466. CheckErr( OCIStmtExecute( m_po->hpContext,
  467. m_po->hpSelect, // prepared by previous function calls
  468. m_po->hpErr,
  469. 0, // 'iters' i.e. max rows to fetch during this call
  470. 0, 
  471. NULL, NULL, OCI_DEFAULT )
  472. );
  473. // If it returns OCI_NO_DATA then no need to define recordset
  474. if ( m_po->hr == OCI_NO_DATA || FAILED(m_po->hr) )
  475. {
  476. return m_po->hr;
  477. }
  478. // Load the types into recordset
  479. int nColumnCount = 0;
  480. while ( m_po->hr == OCI_SUCCESS )
  481. {
  482. // Get pointer to column
  483. void* pFieldAttr = NULL;
  484. HRESULT hrGetNext;
  485. hrGetNext = OCIParamGet( m_po->hpSelect, OCI_HTYPE_STMT,
  486. m_po->hpErr,
  487. &pFieldAttr,
  488. nColumnCount+1 // position
  489. );
  490. if ( hrGetNext != OCI_SUCCESS )
  491. break;
  492. // Increment column count and allocate an OdbField structure
  493. ++nColumnCount;
  494. OdbField* pField = new OdbField;
  495. m_po->rsSelect.paFields.Add( pField );
  496. // Get data type
  497. CheckErr( OCIAttrGet( pFieldAttr, OCI_DTYPE_PARAM,
  498. &pField->wType,
  499. 0,
  500. OCI_ATTR_DATA_TYPE,
  501. m_po->hpErr )
  502. );
  503. // Get data size
  504. CheckErr( OCIAttrGet( pFieldAttr, OCI_DTYPE_PARAM,
  505. &pField->wSize,
  506. 0,
  507. OCI_ATTR_DATA_SIZE,
  508. m_po->hpErr )
  509. );
  510. // Type conversions
  511. if ( pField->wType == SQLT_LNG )
  512. {
  513. // LONG size
  514. pField->wSize = 32760;
  515. }
  516. else if ( pField->wType == SQLT_DAT )
  517. {
  518. // String is bound to DATE
  519. pField->wType = SQLT_STR;
  520. pField->wSize = 50;
  521. }
  522. else if ( pField->wType == SQLT_NUM )
  523. {
  524. // String is bound to NUMBER
  525. pField->wType = SQLT_STR;
  526. pField->wSize += 1; // allow for null-terminator
  527. }
  528. // Get column name
  529. text* pName;
  530. ub4 nNameLen;
  531. CheckErr( OCIAttrGet( pFieldAttr, OCI_DTYPE_PARAM,
  532. (void**)&pName,
  533. &nNameLen,
  534. OCI_ATTR_NAME,
  535. m_po->hpErr )
  536. );
  537. // Set size and name
  538. pField->SetSize( pField->wSize );
  539. pField->csName.Format( "%.*s", nNameLen, pName );
  540. }
  541. // Bind storage for receiving input variables
  542. OCIDefine *pDefn; // to hold pointer to field definition
  543. int iField;
  544. for ( iField=0; iField<m_po->rsSelect.paFields.GetSize(); ++iField )
  545. {
  546. // Get pointer to field structure
  547. OdbField* pField = (OdbField*)m_po->rsSelect.paFields[iField];
  548. // Bind
  549. pDefn = NULL;
  550. CheckErr( OCIDefineByPos( m_po->hpSelect,
  551. &pDefn, // function allocs and gives back a pointer to field definition
  552. m_po->hpErr,
  553. iField+1, // position in select starting at 1
  554. pField->pBuffer, // storage area for field result
  555. pField->wSize, // sizeof storage area
  556. pField->wType, // field type
  557. &pField->nInd, // indp, null indicator
  558. &pField->wLen, // rlenp
  559. NULL,
  560. OCI_DEFAULT )
  561. );
  562. }
  563. // Fetch
  564. FetchNext();
  565. return m_po->hr;
  566. }
  567. HRESULT COdb::FetchNext()
  568. {
  569. //
  570. // Fetch next row of select statement and parse the select list description
  571. //
  572. CheckErr( OCIStmtFetch( m_po->hpSelect,
  573. m_po->hpErr,
  574. 1, // 'nrows' i.e. max rows
  575. OCI_FETCH_NEXT, 
  576. OCI_DEFAULT )
  577. );
  578. // Set result to 0 if no data returned
  579. m_csResults = "";
  580. m_po->rsSelect.m_nRows = 0;
  581. if ( SUCCEEDED(m_po->hr) && m_po->hr != OCI_NO_DATA )
  582. {
  583. // Set number of rows to 1 so IsEOS() will fail
  584. m_po->rsSelect.m_nRows = 1;
  585. // TEMPORARY: create a results string
  586. for ( int iField=0; iField<m_po->rsSelect.paFields.GetSize(); ++iField )
  587. {
  588. OdbField* pField = (OdbField*)m_po->rsSelect.paFields[iField];
  589. if ( ! m_csResults.IsEmpty() )
  590. m_csResults += ", ";
  591. m_csResults += pField->csName + "=" + GetField( iField );
  592. }
  593. }
  594. return m_po->hr;
  595. }
  596. BOOL COdb::IsEOS()
  597. {
  598. //
  599. // Return FALSE if m_nRows is 1, otherwise TRUE
  600. //
  601. BOOL bIsEOS = TRUE;
  602. if ( m_po->rsSelect.m_nRows )
  603. bIsEOS = FALSE;
  604. return bIsEOS;
  605. }
  606. CString COdb::GetField( int iField )
  607. {
  608. //
  609. // Return string from indexed field in rsSelect
  610. //
  611. if ( iField >= 0 && iField < m_po->rsSelect.paFields.GetSize() )
  612. {
  613. OdbField* pField= (OdbField*)m_po->rsSelect.paFields.GetAt( iField );
  614. return pField->Get();
  615. }
  616. return "";
  617. }
  618. CString COdb::GetField( CString csName )
  619. {
  620. //
  621. // Return string from named field in rsSelect
  622. //
  623. for ( int iField = 0; iField < m_po->rsSelect.paFields.GetSize(); ++iField )
  624. {
  625. OdbField* pField= (OdbField*)m_po->rsSelect.paFields.GetAt( iField );
  626. if ( pField->csName.CompareNoCase(csName) == 0 )
  627. return pField->Get();
  628. }
  629. return "";
  630. }
  631. BOOL COdb::GetField( int iField, CString& csName, CString& csValue, BOOL bQuotesIfValueRequires )
  632. {
  633. //
  634. // Return TRUE if iField in range, otherwise FALSE if iField is invalid
  635. // Get name and value, surround value with quotes if flag set
  636. //
  637. if ( iField >= 0 && iField < m_po->rsSelect.paFields.GetSize() )
  638. {
  639. OdbField* pField= (OdbField*)m_po->rsSelect.paFields.GetAt( iField );
  640. if ( bQuotesIfValueRequires && pField->bQuotedOnUpdate )
  641. csValue = "'" + ProcessQuotes(pField->Get()) + "'";
  642. else
  643. csValue = pField->Get();
  644. csName = pField->csName;
  645. return TRUE;
  646. }
  647. return FALSE;
  648. }
  649. void COdb::CleanWhitespace( CString& csStatement )
  650. {
  651. //
  652. // Sometimes crlfs can make a statement execute improperly
  653. // Replace whitespace with spaces because Exec and Select balk at crlfs etc
  654. //
  655. char* pStatement = csStatement.GetBuffer(0);
  656. while ( *pStatement )
  657. {
  658. if ( strchr( "rnt", *pStatement ) )
  659. *pStatement = ' ';
  660. ++pStatement;
  661. }
  662. csStatement.ReleaseBuffer();
  663. }
  664. CString COdb::ProcessQuotes( CString csValue )
  665. {
  666. //
  667. // If there is any chance the value contains quotes...
  668. // call this before placing quotes around the value
  669. //
  670. LPCTSTR szQuote = "'";
  671. // Does the string contain any single quotes?
  672. if ( csValue.Find( szQuote ) >= 0 )
  673. {
  674. // Loop through every occurence of szQuote
  675. int n = csValue.SpanExcluding(szQuote).GetLength();
  676. while ( n < csValue.GetLength() )
  677. {
  678. // Insert the extra quote
  679. csValue = csValue.Left(n) + csValue[n] + csValue.Mid(n);
  680. // Increment n past the two quotes
  681. // This is where we start the next search from
  682. n += 2;
  683. // Note that csValue is now a char longer!
  684. // Check in case quote was the last char in the string
  685. if ( n >= csValue.GetLength() )
  686. break;
  687. // Increment span count to next quote or end of string
  688. n += csValue.Mid(n).SpanExcluding(szQuote).GetLength();
  689. }
  690. }
  691. return csValue;
  692. }