dbcore.cpp 113 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682
  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10. #include "stdafx.h"
  11. #ifdef AFX_DB_SEG
  12. #pragma code_seg(AFX_DB_SEG)
  13. #endif
  14. #ifdef _DEBUG
  15. #undef THIS_FILE
  16. static char THIS_FILE[] = __FILE__;
  17. #endif
  18. #define new DEBUG_NEW
  19. /////////////////////////////////////////////////////////////////////////////
  20. // Global data
  21. #ifdef _DEBUG
  22. BOOL bTraceSql = FALSE;
  23. #endif
  24. AFX_STATIC_DATA const TCHAR _afxODBCTrail[] = _T("ODBC;");
  25. AFX_STATIC_DATA const TCHAR _afxComma[] = _T(",");
  26. AFX_STATIC_DATA const TCHAR _afxLiteralSeparator = '\'';
  27. AFX_STATIC_DATA const TCHAR _afxCall[] = _T("{CALL ");
  28. AFX_STATIC_DATA const TCHAR _afxParamCall[] = _T("{?");
  29. AFX_STATIC_DATA const TCHAR _afxSelect[] = _T("SELECT ");
  30. AFX_STATIC_DATA const TCHAR _afxFrom[] = _T(" FROM ");
  31. AFX_STATIC_DATA const TCHAR _afxWhere[] = _T(" WHERE ");
  32. AFX_STATIC_DATA const TCHAR _afxOrderBy[] = _T(" ORDER BY ");
  33. AFX_STATIC_DATA const TCHAR _afxForUpdate[] = _T(" FOR UPDATE ");
  34. AFX_STATIC_DATA const TCHAR _afxRowFetch[] = _T("State:01S01");
  35. AFX_STATIC_DATA const TCHAR _afxDataTruncated[] = _T("State:01004");
  36. AFX_STATIC_DATA const TCHAR _afxInfoRange[] = _T("State:S1096");
  37. AFX_STATIC_DATA const TCHAR _afxOutOfSequence[] = _T("State:S1010");
  38. AFX_STATIC_DATA const TCHAR _afxDriverNotCapable[] = _T("State:S1C00");
  39. AFX_STATIC_DATA const char _afxODBCDLL[] = "ODBC32.DLL";
  40. /////////////////////////////////////////////////////////////////////////////
  41. // for dynamic load of ODBC32.DLL
  42. #pragma comment(lib, "odbc32.lib")
  43. /////////////////////////////////////////////////////////////////////////////
  44. // CDBException
  45. void AFXAPI AfxThrowDBException(RETCODE nRetCode, CDatabase* pdb, HSTMT hstmt)
  46. {
  47. CDBException* pException = new CDBException(nRetCode);
  48. if (nRetCode == SQL_ERROR && pdb != NULL)
  49. pException->BuildErrorString(pdb, hstmt);
  50. else if (nRetCode > AFX_SQL_ERROR && nRetCode < AFX_SQL_ERROR_MAX)
  51. {
  52. VERIFY(pException->m_strError.LoadString(
  53. AFX_IDP_SQL_FIRST+(nRetCode-AFX_SQL_ERROR)));
  54. TRACE1("%s\n", pException->m_strError);
  55. }
  56. THROW(pException);
  57. }
  58. CDBException::CDBException(RETCODE nRetCode)
  59. {
  60. m_nRetCode = nRetCode;
  61. }
  62. CDBException::~CDBException()
  63. {
  64. }
  65. void CDBException::BuildErrorString(CDatabase* pdb, HSTMT hstmt, BOOL bTrace)
  66. {
  67. ASSERT_VALID(this);
  68. UNUSED(bTrace); // unused in release builds
  69. if (pdb != NULL)
  70. {
  71. SWORD nOutlen;
  72. RETCODE nRetCode;
  73. UCHAR lpszMsg[SQL_MAX_MESSAGE_LENGTH];
  74. UCHAR lpszState[SQL_SQLSTATE_SIZE];
  75. CString strMsg;
  76. CString strState;
  77. SDWORD lNative;
  78. _AFX_DB_STATE* pDbState = _afxDbState;
  79. AFX_SQL_SYNC(::SQLError(pDbState->m_henvAllConnections, pdb->m_hdbc,
  80. hstmt, lpszState, &lNative,
  81. lpszMsg, SQL_MAX_MESSAGE_LENGTH-1, &nOutlen));
  82. strState = lpszState;
  83. // Skip non-errors
  84. while ((nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO) &&
  85. lstrcmp(strState, _T("00000")) != 0)
  86. {
  87. strMsg = lpszMsg;
  88. TCHAR lpszNative[50];
  89. wsprintf(lpszNative, _T(",Native:%ld,Origin:"), lNative);
  90. strState += lpszNative;
  91. // transfer [origin] from message string to StateNativeOrigin string
  92. int nCloseBracket;
  93. int nMsgLength;
  94. while (!strMsg.IsEmpty() &&
  95. strMsg[0] == '[' && (nCloseBracket = strMsg.Find(']')) >= 0)
  96. {
  97. // Skip ']'
  98. nCloseBracket++;
  99. strState += strMsg.Left(nCloseBracket);
  100. nMsgLength = strMsg.GetLength();
  101. // Skip ' ', if present
  102. if (nCloseBracket < nMsgLength && strMsg[nCloseBracket] == ' ')
  103. nCloseBracket++;
  104. strMsg = strMsg.Right(nMsgLength - nCloseBracket);
  105. }
  106. strState += _T("\n");
  107. m_strStateNativeOrigin += _T("State:") + strState;
  108. m_strError += strMsg + _T("\n");
  109. #ifdef _DEBUG
  110. if (bTrace)
  111. {
  112. TraceErrorMessage(strMsg);
  113. TraceErrorMessage(_T("State:") + strState);
  114. }
  115. #endif // _DEBUG
  116. AFX_SQL_SYNC(::SQLError(pDbState->m_henvAllConnections,
  117. pdb->m_hdbc, hstmt, lpszState, &lNative,
  118. lpszMsg, SQL_MAX_MESSAGE_LENGTH-1, &nOutlen));
  119. strState = lpszState;
  120. }
  121. }
  122. }
  123. BOOL CDBException::GetErrorMessage(LPTSTR lpszError, UINT nMaxError,
  124. PUINT pnHelpContext /* = NULL */)
  125. {
  126. ASSERT(lpszError != NULL && AfxIsValidString(lpszError, nMaxError));
  127. if (pnHelpContext != NULL)
  128. *pnHelpContext = 0;
  129. lstrcpyn(lpszError, m_strError, nMaxError-1);
  130. lpszError[nMaxError-1] = '\0';
  131. return TRUE;
  132. }
  133. #ifdef _DEBUG
  134. void CDBException::TraceErrorMessage(LPCTSTR szTrace) const
  135. {
  136. CString strTrace = szTrace;
  137. if (strTrace.GetLength() <= 80)
  138. TRACE1("%s\n", strTrace);
  139. else
  140. {
  141. // Display 80 chars/line
  142. while (strTrace.GetLength() > 80)
  143. {
  144. TRACE1("%s\n", strTrace.Left(80));
  145. strTrace = strTrace.Right(strTrace.GetLength() - 80);
  146. }
  147. TRACE1("%s\n", strTrace);
  148. }
  149. }
  150. #endif // _DEBUG
  151. void CDBException::Empty()
  152. {
  153. m_strError.Empty();
  154. m_strStateNativeOrigin.Empty();
  155. }
  156. /////////////////////////////////////////////////////////////////////////////
  157. // Global helper
  158. HENV AFXAPI AfxGetHENV()
  159. {
  160. _AFX_DB_STATE* pDbState = _afxDbState;
  161. return pDbState->m_henvAllConnections;
  162. }
  163. /////////////////////////////////////////////////////////////////////////////
  164. // CDatabase implementation
  165. CDatabase::CDatabase()
  166. {
  167. m_hdbc = SQL_NULL_HDBC;
  168. m_hstmt = SQL_NULL_HSTMT;
  169. m_bUpdatable = FALSE;
  170. m_bTransactions = FALSE;
  171. DEBUG_ONLY(m_bTransactionPending = FALSE);
  172. m_dwLoginTimeout = DEFAULT_LOGIN_TIMEOUT;
  173. m_dwQueryTimeout = DEFAULT_QUERY_TIMEOUT;
  174. m_bStripTrailingSpaces = FALSE;
  175. m_bIncRecordCountOnAdd = FALSE;
  176. m_bAddForUpdate = FALSE;
  177. }
  178. CDatabase::~CDatabase()
  179. {
  180. ASSERT_VALID(this);
  181. Free();
  182. }
  183. BOOL CDatabase::Open(LPCTSTR lpszDSN, BOOL bExclusive,
  184. BOOL bReadonly, LPCTSTR lpszConnect, BOOL bUseCursorLib)
  185. {
  186. ASSERT(lpszDSN == NULL || AfxIsValidString(lpszDSN));
  187. ASSERT(lpszConnect == NULL || AfxIsValidString(lpszConnect));
  188. CString strConnect;
  189. if (lpszConnect != NULL)
  190. strConnect = lpszConnect;
  191. // For VB/Access compatibility, require "ODBC;" (or "odbc;")
  192. // prefix to the connect string
  193. if (_tcsnicmp(strConnect, _afxODBCTrail, lstrlen(_afxODBCTrail)) != 0)
  194. {
  195. TRACE0("Error: Missing 'ODBC' prefix on connect string.\n");
  196. return FALSE;
  197. }
  198. // Strip "ODBC;"
  199. strConnect = strConnect.Right(strConnect.GetLength()
  200. - lstrlen(_afxODBCTrail));
  201. if (lpszDSN != NULL && lstrlen(lpszDSN) != 0)
  202. {
  203. // Append "DSN=" lpszDSN
  204. strConnect += _T(";DSN=");
  205. strConnect += lpszDSN;
  206. }
  207. DWORD dwOptions = 0;
  208. if (bExclusive)
  209. dwOptions |= openExclusive;
  210. if (bReadonly)
  211. dwOptions |= openReadOnly;
  212. if (bUseCursorLib)
  213. dwOptions |= useCursorLib;
  214. return OpenEx(strConnect, dwOptions);
  215. }
  216. BOOL CDatabase::OpenEx(LPCTSTR lpszConnectString, DWORD dwOptions)
  217. {
  218. ASSERT_VALID(this);
  219. ASSERT(lpszConnectString == NULL || AfxIsValidString(lpszConnectString));
  220. ASSERT(!(dwOptions & noOdbcDialog && dwOptions & forceOdbcDialog));
  221. // Exclusive access not supported.
  222. ASSERT(!(dwOptions & openExclusive));
  223. m_bUpdatable = !(dwOptions & openReadOnly);
  224. TRY
  225. {
  226. m_strConnect = lpszConnectString;
  227. // Allocate the HDBC and make connection
  228. AllocConnect(dwOptions);
  229. if(!Connect(dwOptions))
  230. return FALSE;
  231. // Verify support for required functionality and cache info
  232. VerifyConnect();
  233. GetConnectInfo();
  234. }
  235. CATCH_ALL(e)
  236. {
  237. Free();
  238. THROW_LAST();
  239. }
  240. END_CATCH_ALL
  241. return TRUE;
  242. }
  243. void CDatabase::ExecuteSQL(LPCTSTR lpszSQL)
  244. {
  245. USES_CONVERSION;
  246. RETCODE nRetCode;
  247. HSTMT hstmt;
  248. ASSERT_VALID(this);
  249. ASSERT(AfxIsValidString(lpszSQL));
  250. AFX_SQL_SYNC(::SQLAllocStmt(m_hdbc, &hstmt));
  251. if (!CheckHstmt(nRetCode, hstmt))
  252. AfxThrowDBException(nRetCode, this, hstmt);
  253. TRY
  254. {
  255. OnSetOptions(hstmt);
  256. // Give derived CDatabase classes option to use parameters
  257. BindParameters(hstmt);
  258. AFX_ODBC_CALL(::SQLExecDirect(hstmt,
  259. (UCHAR*)T2A((LPTSTR)lpszSQL), SQL_NTS));
  260. if (!CheckHstmt(nRetCode, hstmt))
  261. AfxThrowDBException(nRetCode, this, hstmt);
  262. else
  263. {
  264. do
  265. {
  266. SWORD nResultColumns;
  267. AFX_ODBC_CALL(::SQLNumResultCols(hstmt, &nResultColumns));
  268. if (nResultColumns != 0)
  269. {
  270. do
  271. {
  272. AFX_ODBC_CALL(::SQLFetch(hstmt));
  273. } while (CheckHstmt(nRetCode, hstmt) &&
  274. nRetCode != SQL_NO_DATA_FOUND);
  275. }
  276. AFX_ODBC_CALL(::SQLMoreResults(hstmt));
  277. } while (CheckHstmt(nRetCode, hstmt) &&
  278. nRetCode != SQL_NO_DATA_FOUND);
  279. }
  280. }
  281. CATCH_ALL(e)
  282. {
  283. ::SQLCancel(hstmt);
  284. AFX_SQL_SYNC(::SQLFreeStmt(hstmt, SQL_DROP));
  285. THROW_LAST();
  286. }
  287. END_CATCH_ALL
  288. AFX_SQL_SYNC(::SQLFreeStmt(hstmt, SQL_DROP));
  289. }
  290. // Shutdown pending query for CDatabase's private m_hstmt
  291. void CDatabase::Cancel()
  292. {
  293. ASSERT_VALID(this);
  294. ASSERT(m_hdbc != SQL_NULL_HDBC);
  295. ::SQLCancel(m_hstmt);
  296. }
  297. // Disconnect connection
  298. void CDatabase::Close()
  299. {
  300. ASSERT_VALID(this);
  301. // Close any open recordsets
  302. AfxLockGlobals(CRIT_ODBC);
  303. TRY
  304. {
  305. while (!m_listRecordsets.IsEmpty())
  306. {
  307. CRecordset* pSet = (CRecordset*)m_listRecordsets.GetHead();
  308. pSet->Close(); // will implicitly remove from list
  309. pSet->m_pDatabase = NULL;
  310. }
  311. }
  312. CATCH_ALL(e)
  313. {
  314. AfxUnlockGlobals(CRIT_ODBC);
  315. THROW_LAST();
  316. }
  317. END_CATCH_ALL
  318. AfxUnlockGlobals(CRIT_ODBC);
  319. if (m_hdbc != SQL_NULL_HDBC)
  320. {
  321. RETCODE nRetCode;
  322. AFX_SQL_SYNC(::SQLDisconnect(m_hdbc));
  323. AFX_SQL_SYNC(::SQLFreeConnect(m_hdbc));
  324. m_hdbc = SQL_NULL_HDBC;
  325. _AFX_DB_STATE* pDbState = _afxDbState;
  326. AfxLockGlobals(CRIT_ODBC);
  327. ASSERT(pDbState->m_nAllocatedConnections != 0);
  328. pDbState->m_nAllocatedConnections--;
  329. AfxUnlockGlobals(CRIT_ODBC);
  330. }
  331. }
  332. // Silently disconnect and free all ODBC resources. Don't throw any exceptions
  333. void CDatabase::Free()
  334. {
  335. ASSERT_VALID(this);
  336. // Trap failures upon close
  337. TRY
  338. {
  339. Close();
  340. }
  341. CATCH_ALL(e)
  342. {
  343. // Nothing we can do
  344. TRACE0("Error: exception by CDatabase::Close() ignored in CDatabase::Free().\n");
  345. DELETE_EXCEPTION(e);
  346. }
  347. END_CATCH_ALL
  348. // free henv if refcount goes to 0
  349. _AFX_DB_STATE* pDbState = _afxDbState;
  350. AfxLockGlobals(CRIT_ODBC);
  351. if (pDbState->m_henvAllConnections != SQL_NULL_HENV)
  352. {
  353. ASSERT(pDbState->m_nAllocatedConnections >= 0);
  354. if (pDbState->m_nAllocatedConnections == 0)
  355. {
  356. // free last connection - release HENV
  357. RETCODE nRetCodeEnv = ::SQLFreeEnv(pDbState->m_henvAllConnections);
  358. #ifdef _DEBUG
  359. if (nRetCodeEnv != SQL_SUCCESS)
  360. // Nothing we can do
  361. TRACE0("Error: SQLFreeEnv failure ignored in CDatabase::Free().\n");
  362. #endif
  363. pDbState->m_henvAllConnections = SQL_NULL_HENV;
  364. }
  365. }
  366. AfxUnlockGlobals(CRIT_ODBC);
  367. }
  368. void CDatabase::OnSetOptions(HSTMT hstmt)
  369. {
  370. RETCODE nRetCode;
  371. ASSERT_VALID(this);
  372. ASSERT(m_hdbc != SQL_NULL_HDBC);
  373. if (m_dwQueryTimeout != -1)
  374. {
  375. // Attempt to set query timeout. Ignore failure
  376. AFX_SQL_SYNC(::SQLSetStmtOption(hstmt, SQL_QUERY_TIMEOUT,
  377. m_dwQueryTimeout));
  378. if (!Check(nRetCode))
  379. // don't attempt it again
  380. m_dwQueryTimeout = (DWORD)-1;
  381. }
  382. }
  383. CString CDatabase::GetDatabaseName() const
  384. {
  385. ASSERT_VALID(this);
  386. ASSERT(m_hdbc != SQL_NULL_HDBC);
  387. char szName[MAX_TNAME_LEN];
  388. SWORD nResult;
  389. RETCODE nRetCode;
  390. AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_DATABASE_NAME,
  391. szName, _countof(szName), &nResult));
  392. if (!Check(nRetCode))
  393. szName[0] = '\0';
  394. return szName;
  395. }
  396. BOOL CDatabase::BeginTrans()
  397. {
  398. ASSERT_VALID(this);
  399. ASSERT(m_hdbc != SQL_NULL_HDBC);
  400. if (!m_bTransactions)
  401. return FALSE;
  402. // Only 1 level of transactions supported
  403. #ifdef _DEBUG
  404. ASSERT(!m_bTransactionPending);
  405. #endif
  406. RETCODE nRetCode;
  407. AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc, SQL_AUTOCOMMIT,
  408. SQL_AUTOCOMMIT_OFF));
  409. DEBUG_ONLY(m_bTransactionPending = TRUE);
  410. return Check(nRetCode);
  411. }
  412. BOOL CDatabase::CommitTrans()
  413. {
  414. ASSERT_VALID(this);
  415. ASSERT(m_hdbc != SQL_NULL_HDBC);
  416. if (!m_bTransactions)
  417. return FALSE;
  418. // BeginTrans must be called first
  419. #ifdef _DEBUG
  420. ASSERT(m_bTransactionPending);
  421. #endif
  422. _AFX_DB_STATE* pDbState = _afxDbState;
  423. RETCODE nRetCode;
  424. AFX_SQL_SYNC(::SQLTransact(pDbState->m_henvAllConnections, m_hdbc, SQL_COMMIT));
  425. BOOL bSuccess = Check(nRetCode);
  426. // Turn back on auto commit
  427. AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc, SQL_AUTOCOMMIT,
  428. SQL_AUTOCOMMIT_ON));
  429. DEBUG_ONLY(m_bTransactionPending = FALSE);
  430. return bSuccess;
  431. }
  432. BOOL CDatabase::Rollback()
  433. {
  434. ASSERT_VALID(this);
  435. ASSERT(m_hdbc != SQL_NULL_HDBC);
  436. if (!m_bTransactions)
  437. return FALSE;
  438. // BeginTrans must be called first
  439. #ifdef _DEBUG
  440. ASSERT(m_bTransactionPending);
  441. #endif
  442. _AFX_DB_STATE* pDbState = _afxDbState;
  443. RETCODE nRetCode;
  444. AFX_SQL_SYNC(::SQLTransact(pDbState->m_henvAllConnections, m_hdbc, SQL_ROLLBACK));
  445. BOOL bSuccess = Check(nRetCode);
  446. // Turn back on auto commit
  447. AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc, SQL_AUTOCOMMIT,
  448. SQL_AUTOCOMMIT_ON));
  449. DEBUG_ONLY(m_bTransactionPending = FALSE);
  450. return bSuccess;
  451. }
  452. // Screen for errors.
  453. BOOL CDatabase::Check(RETCODE nRetCode) const
  454. {
  455. return CheckHstmt(nRetCode, SQL_NULL_HSTMT);
  456. }
  457. BOOL CDatabase::CheckHstmt(RETCODE nRetCode, HSTMT hstmt) const
  458. {
  459. ASSERT_VALID(this);
  460. UNUSED(hstmt);
  461. switch (nRetCode)
  462. {
  463. case SQL_SUCCESS_WITH_INFO:
  464. #ifdef _DEBUG
  465. if (afxTraceFlags & traceDatabase)
  466. {
  467. CDBException e(nRetCode);
  468. TRACE0("Warning: ODBC Success With Info, ");
  469. e.BuildErrorString((CDatabase*)this, hstmt);
  470. }
  471. #endif // _DEBUG
  472. // Fall through
  473. case SQL_SUCCESS:
  474. case SQL_NO_DATA_FOUND:
  475. return TRUE;
  476. }
  477. return FALSE;
  478. }
  479. //////////////////////////////////////////////////////////////////////////////
  480. // CDatabase internal functions
  481. //Replace brackets in SQL string with SQL_IDENTIFIER_QUOTE_CHAR
  482. void CDatabase::ReplaceBrackets(LPTSTR lpchSQL)
  483. {
  484. BOOL bInLiteral = FALSE;
  485. LPTSTR lpchNewSQL = lpchSQL;
  486. while (*lpchSQL != '\0')
  487. {
  488. if (*lpchSQL == _afxLiteralSeparator)
  489. {
  490. // Handle escaped literal
  491. if (*_tcsinc(lpchSQL) == _afxLiteralSeparator)
  492. {
  493. *lpchNewSQL = *lpchSQL;
  494. lpchSQL = _tcsinc(lpchSQL);
  495. lpchNewSQL = _tcsinc(lpchNewSQL);
  496. }
  497. else
  498. bInLiteral = !bInLiteral;
  499. *lpchNewSQL = *lpchSQL;
  500. }
  501. else if (!bInLiteral && (*lpchSQL == '['))
  502. {
  503. if (*_tcsinc(lpchSQL) == '[')
  504. {
  505. // Handle escaped left bracket by inserting one '['
  506. *lpchNewSQL = *lpchSQL;
  507. lpchSQL = _tcsinc(lpchSQL);
  508. }
  509. else
  510. *lpchNewSQL = m_chIDQuoteChar;
  511. }
  512. else if (!bInLiteral && (*lpchSQL == ']'))
  513. {
  514. if (*_tcsinc(lpchSQL) == ']')
  515. {
  516. // Handle escaped right bracket by inserting one ']'
  517. *lpchNewSQL = *lpchSQL;
  518. lpchSQL = _tcsinc(lpchSQL);
  519. }
  520. else
  521. *lpchNewSQL = m_chIDQuoteChar;
  522. }
  523. else
  524. *lpchNewSQL = *lpchSQL;
  525. lpchSQL = _tcsinc(lpchSQL);
  526. lpchNewSQL = _tcsinc(lpchNewSQL);
  527. }
  528. }
  529. // Allocate an henv (first time called) and hdbc
  530. void CDatabase::AllocConnect(DWORD dwOptions)
  531. {
  532. ASSERT_VALID(this);
  533. if (m_hdbc != SQL_NULL_HDBC)
  534. return;
  535. _AFX_DB_STATE* pDbState = _afxDbState;
  536. RETCODE nRetCode;
  537. AfxLockGlobals(CRIT_ODBC);
  538. if (pDbState->m_henvAllConnections == SQL_NULL_HENV)
  539. {
  540. ASSERT(pDbState->m_nAllocatedConnections == 0);
  541. // need to allocate an environment for first connection
  542. AFX_SQL_SYNC(::SQLAllocEnv(&pDbState->m_henvAllConnections));
  543. if (!Check(nRetCode))
  544. {
  545. AfxUnlockGlobals(CRIT_ODBC);
  546. AfxThrowMemoryException(); // fatal
  547. }
  548. }
  549. ASSERT(pDbState->m_henvAllConnections != SQL_NULL_HENV);
  550. AFX_SQL_SYNC(::SQLAllocConnect(pDbState->m_henvAllConnections, &m_hdbc));
  551. if (!Check(nRetCode))
  552. {
  553. AfxUnlockGlobals(CRIT_ODBC);
  554. ThrowDBException(nRetCode); // fatal
  555. }
  556. pDbState->m_nAllocatedConnections++; // allocated at least
  557. AfxUnlockGlobals(CRIT_ODBC);
  558. #ifdef _DEBUG
  559. if (bTraceSql)
  560. {
  561. ::SQLSetConnectOption(m_hdbc, SQL_OPT_TRACEFILE,
  562. (DWORD)"odbccall.txt");
  563. ::SQLSetConnectOption(m_hdbc, SQL_OPT_TRACE, 1);
  564. }
  565. #endif // _DEBUG
  566. AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc, SQL_LOGIN_TIMEOUT,
  567. m_dwLoginTimeout));
  568. #ifdef _DEBUG
  569. if (nRetCode != SQL_SUCCESS && nRetCode != SQL_SUCCESS_WITH_INFO &&
  570. (afxTraceFlags & traceDatabase))
  571. TRACE0("Warning: Failure setting login timeout.\n");
  572. #endif
  573. if (!m_bUpdatable)
  574. {
  575. AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc, SQL_ACCESS_MODE,
  576. SQL_MODE_READ_ONLY));
  577. #ifdef _DEBUG
  578. if (nRetCode != SQL_SUCCESS && nRetCode != SQL_SUCCESS_WITH_INFO &&
  579. (afxTraceFlags & traceDatabase))
  580. TRACE0("Warning: Failure setting read only access mode.\n");
  581. #endif
  582. }
  583. // Turn on cursor lib support
  584. if (dwOptions & useCursorLib)
  585. {
  586. AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc,
  587. SQL_ODBC_CURSORS, SQL_CUR_USE_ODBC));
  588. // With cursor library added records immediately in result set
  589. m_bIncRecordCountOnAdd = TRUE;
  590. }
  591. }
  592. BOOL CDatabase::Connect(DWORD dwOptions)
  593. {
  594. USES_CONVERSION;
  595. HWND hWndTop;
  596. HWND hWnd = CWnd::GetSafeOwner_(NULL, &hWndTop);
  597. if (hWnd == NULL)
  598. hWnd = ::GetDesktopWindow();
  599. UCHAR szConnectOutput[MAX_CONNECT_LEN];
  600. RETCODE nRetCode;
  601. SWORD nResult;
  602. UWORD wConnectOption = SQL_DRIVER_COMPLETE;
  603. if (dwOptions & noOdbcDialog)
  604. wConnectOption = SQL_DRIVER_NOPROMPT;
  605. else if (dwOptions & forceOdbcDialog)
  606. wConnectOption = SQL_DRIVER_PROMPT;
  607. AFX_SQL_SYNC(::SQLDriverConnect(m_hdbc, hWnd,
  608. (UCHAR*)T2A((LPTSTR)(LPCTSTR)m_strConnect), SQL_NTS,
  609. szConnectOutput, _countof(szConnectOutput),
  610. &nResult, wConnectOption));
  611. if (hWndTop != NULL)
  612. ::EnableWindow(hWndTop, TRUE);
  613. // If user hit 'Cancel'
  614. if (nRetCode == SQL_NO_DATA_FOUND)
  615. {
  616. Free();
  617. return FALSE;
  618. }
  619. if (!Check(nRetCode))
  620. {
  621. #ifdef _DEBUG
  622. if (hWnd == NULL)
  623. TRACE0("Error: No default window (AfxGetApp()->m_pMainWnd) for SQLDriverConnect.\n");
  624. #endif
  625. ThrowDBException(nRetCode);
  626. }
  627. // Connect strings must have "ODBC;"
  628. m_strConnect = _afxODBCTrail;
  629. // Save connect string returned from ODBC
  630. m_strConnect += (char*)szConnectOutput;
  631. return TRUE;
  632. }
  633. void CDatabase::VerifyConnect()
  634. {
  635. RETCODE nRetCode;
  636. SWORD nResult;
  637. SWORD nAPIConformance;
  638. AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_ODBC_API_CONFORMANCE,
  639. &nAPIConformance, sizeof(nAPIConformance), &nResult));
  640. if (!Check(nRetCode))
  641. ThrowDBException(nRetCode);
  642. if (nAPIConformance < SQL_OAC_LEVEL1)
  643. ThrowDBException(AFX_SQL_ERROR_API_CONFORMANCE);
  644. SWORD nSQLConformance;
  645. AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_ODBC_SQL_CONFORMANCE,
  646. &nSQLConformance, sizeof(nSQLConformance), &nResult));
  647. if (!Check(nRetCode))
  648. ThrowDBException(nRetCode);
  649. if (nSQLConformance < SQL_OSC_MINIMUM)
  650. ThrowDBException(AFX_SQL_ERROR_SQL_CONFORMANCE);
  651. }
  652. void CDatabase::GetConnectInfo()
  653. {
  654. RETCODE nRetCode;
  655. SWORD nResult;
  656. // Reset the database update options
  657. m_dwUpdateOptions = 0;
  658. // Check for SQLSetPos support
  659. UDWORD dwDriverPosOperations;
  660. AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_POS_OPERATIONS,
  661. &dwDriverPosOperations, sizeof(dwDriverPosOperations), &nResult));
  662. if (Check(nRetCode) &&
  663. (dwDriverPosOperations & SQL_POS_UPDATE) &&
  664. (dwDriverPosOperations & SQL_POS_DELETE) &&
  665. (dwDriverPosOperations & SQL_POS_ADD))
  666. m_dwUpdateOptions |= AFX_SQL_SETPOSUPDATES;
  667. // Check for positioned update SQL support
  668. UDWORD dwPositionedStatements;
  669. AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_POSITIONED_STATEMENTS,
  670. &dwPositionedStatements, sizeof(dwPositionedStatements),
  671. &nResult));
  672. if (Check(nRetCode) &&
  673. (dwPositionedStatements & SQL_PS_POSITIONED_DELETE) &&
  674. (dwPositionedStatements & SQL_PS_POSITIONED_UPDATE))
  675. m_dwUpdateOptions |= AFX_SQL_POSITIONEDSQL;
  676. // Check for transaction support
  677. SWORD nTxnCapable;
  678. AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_TXN_CAPABLE, &nTxnCapable,
  679. sizeof(nTxnCapable), &nResult));
  680. if (Check(nRetCode) && nTxnCapable != SQL_TC_NONE)
  681. m_bTransactions = TRUE;
  682. // Cache the effect of transactions on cursors
  683. AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_CURSOR_COMMIT_BEHAVIOR,
  684. &m_nCursorCommitBehavior, sizeof(m_nCursorCommitBehavior),
  685. &nResult));
  686. if (!Check(nRetCode))
  687. m_nCursorCommitBehavior = SQL_ERROR;
  688. AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_CURSOR_ROLLBACK_BEHAVIOR,
  689. &m_nCursorRollbackBehavior, sizeof(m_nCursorRollbackBehavior),
  690. &nResult));
  691. if (!Check(nRetCode))
  692. m_nCursorRollbackBehavior = SQL_ERROR;
  693. // Cache bookmark attributes
  694. AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_BOOKMARK_PERSISTENCE,
  695. &m_dwBookmarkAttributes, sizeof(m_dwBookmarkAttributes),
  696. &nResult));
  697. Check(nRetCode);
  698. // Check for SQLGetData support req'd by RFX_LongBinary
  699. UDWORD dwGetDataExtensions;
  700. AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_GETDATA_EXTENSIONS,
  701. &dwGetDataExtensions, sizeof(dwGetDataExtensions),
  702. &nResult));
  703. if (!Check(nRetCode))
  704. dwGetDataExtensions = 0;
  705. if (dwGetDataExtensions & SQL_GD_BOUND)
  706. m_dwUpdateOptions |= AFX_SQL_GDBOUND;
  707. if (m_bUpdatable)
  708. {
  709. // Make sure data source is Updatable
  710. char szReadOnly[10];
  711. AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_DATA_SOURCE_READ_ONLY,
  712. szReadOnly, _countof(szReadOnly), &nResult));
  713. if (Check(nRetCode) && nResult == 1)
  714. m_bUpdatable = !(lstrcmpA(szReadOnly, "Y") == 0);
  715. else
  716. m_bUpdatable = FALSE;
  717. #ifdef _DEBUG
  718. if (!m_bUpdatable && (afxTraceFlags & traceDatabase))
  719. TRACE0("Warning: data source is readonly.\n");
  720. #endif
  721. }
  722. else
  723. {
  724. // Make data source is !Updatable
  725. AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc,
  726. SQL_ACCESS_MODE, SQL_MODE_READ_ONLY));
  727. }
  728. // Cache the quote char to use when constructing SQL
  729. char szIDQuoteChar[2];
  730. AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_IDENTIFIER_QUOTE_CHAR,
  731. szIDQuoteChar, _countof(szIDQuoteChar), &nResult));
  732. if (Check(nRetCode) && nResult == 1)
  733. m_chIDQuoteChar = szIDQuoteChar[0];
  734. else
  735. m_chIDQuoteChar = ' ';
  736. #ifdef _DEBUG
  737. if (afxTraceFlags & traceDatabase)
  738. {
  739. char szInfo[64];
  740. AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_DBMS_NAME,
  741. szInfo, _countof(szInfo), &nResult));
  742. if (Check(nRetCode))
  743. {
  744. CString strInfo = szInfo;
  745. TRACE1("DBMS: %s\n", strInfo);
  746. AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_DBMS_VER,
  747. szInfo, _countof(szInfo), &nResult));
  748. if (Check(nRetCode))
  749. {
  750. strInfo = szInfo;
  751. TRACE1(", Version: %s\n", strInfo);
  752. }
  753. }
  754. }
  755. #endif // _DEBUG
  756. }
  757. void CDatabase::BindParameters(HSTMT /* hstmt */)
  758. {
  759. // Must override and call SQLBindParameter directly
  760. }
  761. //////////////////////////////////////////////////////////////////////////////
  762. // CDatabase diagnostics
  763. #ifdef _DEBUG
  764. void CDatabase::AssertValid() const
  765. {
  766. CObject::AssertValid();
  767. }
  768. void CDatabase::Dump(CDumpContext& dc) const
  769. {
  770. CObject::Dump(dc);
  771. dc << "m_hdbc = " << m_hdbc;
  772. dc << "\nm_strConnect = " << m_strConnect;
  773. dc << "\nm_bUpdatable = " << m_bUpdatable;
  774. dc << "\nm_bTransactions = " << m_bTransactions;
  775. dc << "\nm_bTransactionPending = " << m_bTransactionPending;
  776. dc << "\nm_dwLoginTimeout = " << m_dwLoginTimeout;
  777. dc << "\nm_dwQueryTimeout = " << m_dwQueryTimeout;
  778. if (dc.GetDepth() > 0)
  779. {
  780. _AFX_DB_STATE* pDbState = _afxDbState;
  781. dc << "\nwith env:";
  782. dc << "\n\tnAllocated = " << pDbState->m_nAllocatedConnections;
  783. dc << "\n\thenvAllConnections = " << pDbState->m_henvAllConnections;
  784. }
  785. dc << "\n";
  786. }
  787. #endif // _DEBUG
  788. //////////////////////////////////////////////////////////////////////////////
  789. // CRecordset helpers
  790. void AFXAPI AfxSetCurrentRecord(long* plCurrentRecord, long nRows, RETCODE nRetCode);
  791. void AFXAPI AfxSetRecordCount(long* plRecordCount, long lCurrentRecord,
  792. BOOL bEOFSeen, RETCODE nRetCode);
  793. //////////////////////////////////////////////////////////////////////////////
  794. // CRecordset
  795. CRecordset::CRecordset(CDatabase* pDatabase)
  796. {
  797. ASSERT(pDatabase == NULL || AfxIsValidAddress(pDatabase, sizeof(CDatabase)));
  798. m_pDatabase = pDatabase;
  799. m_nOpenType = snapshot;
  800. m_lOpen = AFX_RECORDSET_STATUS_UNKNOWN;
  801. m_nEditMode = noMode;
  802. m_nDefaultType = snapshot;
  803. m_dwOptions = none;
  804. m_bAppendable = FALSE;
  805. m_bUpdatable = FALSE;
  806. m_bScrollable = FALSE;
  807. m_bRecordsetDb = FALSE;
  808. m_bRebindParams = FALSE;
  809. m_bLongBinaryColumns = FALSE;
  810. m_nLockMode = optimistic;
  811. m_dwInitialGetDataLen = 0;
  812. m_rgODBCFieldInfos = NULL;
  813. m_rgFieldInfos = NULL;
  814. m_rgRowStatus = NULL;
  815. m_dwRowsetSize = 25;
  816. m_dwAllocatedRowsetSize = 0;
  817. m_nFields = 0;
  818. m_nParams = 0;
  819. m_nFieldsBound = 0;
  820. m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED;
  821. m_lRecordCount = 0;
  822. m_bUseUpdateSQL = FALSE;
  823. m_bUseODBCCursorLib = FALSE;
  824. m_nResultCols = -1;
  825. m_bCheckCacheForDirtyFields = TRUE;
  826. m_pbFieldFlags = NULL;
  827. m_pbParamFlags = NULL;
  828. m_plParamLength = NULL;
  829. m_pvFieldProxy = NULL;
  830. m_pvParamProxy = NULL;
  831. m_nProxyFields = 0;
  832. m_nProxyParams = 0;
  833. m_hstmtUpdate = SQL_NULL_HSTMT;
  834. m_hstmt = SQL_NULL_HSTMT;
  835. if (m_pDatabase != NULL && m_pDatabase->IsOpen())
  836. {
  837. ASSERT_VALID(m_pDatabase);
  838. TRY
  839. {
  840. RETCODE nRetCode;
  841. AFX_SQL_SYNC(::SQLAllocStmt(m_pDatabase->m_hdbc, &m_hstmt));
  842. if (!Check(nRetCode))
  843. ThrowDBException(SQL_INVALID_HANDLE);
  844. // Add to list of CRecordsets with alloced hstmts
  845. AfxLockGlobals(CRIT_ODBC);
  846. TRY
  847. {
  848. m_pDatabase->m_listRecordsets.AddHead(this);
  849. }
  850. CATCH_ALL(e)
  851. {
  852. AfxUnlockGlobals(CRIT_ODBC);
  853. THROW_LAST();
  854. }
  855. END_CATCH_ALL
  856. AfxUnlockGlobals(CRIT_ODBC);
  857. }
  858. CATCH_ALL(e)
  859. {
  860. ASSERT(m_hstmt == SQL_NULL_HSTMT);
  861. DELETE_EXCEPTION(e);
  862. }
  863. END_CATCH_ALL
  864. }
  865. }
  866. CRecordset::~CRecordset()
  867. {
  868. ASSERT_VALID(this);
  869. TRY
  870. {
  871. if (m_hstmt != NULL)
  872. {
  873. #ifdef _DEBUG
  874. if (m_dwOptions & useMultiRowFetch && afxTraceFlags & traceDatabase)
  875. {
  876. TRACE0("\nWARNING: Close called implicitly from destructor.");
  877. TRACE0("\nUse of multi row fetch requires explicit call");
  878. TRACE0("\nto Close or memory leaks will result.\n");
  879. }
  880. #endif
  881. Close();
  882. }
  883. if (m_bRecordsetDb)
  884. delete m_pDatabase;
  885. m_pDatabase = NULL;
  886. }
  887. CATCH_ALL(e)
  888. {
  889. // Nothing we can do
  890. TRACE0("Error: Exception ignored in ~CRecordset().\n");
  891. DELETE_EXCEPTION(e);
  892. }
  893. END_CATCH_ALL
  894. }
  895. BOOL CRecordset::Open(UINT nOpenType, LPCTSTR lpszSQL, DWORD dwOptions)
  896. {
  897. ASSERT(!IsOpen());
  898. ASSERT_VALID(this);
  899. ASSERT(lpszSQL == NULL || AfxIsValidString(lpszSQL));
  900. ASSERT(nOpenType == AFX_DB_USE_DEFAULT_TYPE ||
  901. nOpenType == dynaset || nOpenType == snapshot ||
  902. nOpenType == forwardOnly || nOpenType == dynamic);
  903. ASSERT(!(dwOptions & readOnly && dwOptions & appendOnly));
  904. // Can only use optimizeBulkAdd with appendOnly recordsets
  905. ASSERT((dwOptions & optimizeBulkAdd && dwOptions & appendOnly) ||
  906. !(dwOptions & optimizeBulkAdd));
  907. // forwardOnly recordsets have limited functionality
  908. ASSERT(!(nOpenType == forwardOnly && dwOptions & skipDeletedRecords));
  909. // Cache state info and allocate hstmt
  910. SetState(nOpenType, lpszSQL, dwOptions);
  911. if(!AllocHstmt())
  912. return FALSE;
  913. // Check if bookmarks upported (CanBookmark depends on open DB)
  914. ASSERT(dwOptions & useBookmarks ? CanBookmark() : TRUE);
  915. TRY
  916. {
  917. OnSetOptions(m_hstmt);
  918. // Allocate the field/param status arrays, if necessary
  919. BOOL bUnbound = FALSE;
  920. if (m_nFields > 0 || m_nParams > 0)
  921. AllocStatusArrays();
  922. else
  923. bUnbound = TRUE;
  924. // Build SQL and prep/execute or just execute direct
  925. BuildSQL(lpszSQL);
  926. PrepareAndExecute();
  927. // Cache some field info and prepare the rowset
  928. AllocAndCacheFieldInfo();
  929. AllocRowset();
  930. // If late binding, still need to allocate status arrays
  931. if (bUnbound && (m_nFields > 0 || m_nParams > 0))
  932. AllocStatusArrays();
  933. // Give derived classes a call before binding
  934. PreBindFields();
  935. // Fetch the first row of data
  936. MoveNext();
  937. // If EOF, then result set empty, so set BOF as well
  938. m_bBOF = m_bEOF;
  939. }
  940. CATCH_ALL(e)
  941. {
  942. Close();
  943. THROW_LAST();
  944. }
  945. END_CATCH_ALL
  946. return TRUE;
  947. }
  948. void CRecordset::Close()
  949. {
  950. ASSERT_VALID(this);
  951. // Can't close if database has been deleted
  952. ASSERT(m_pDatabase != NULL);
  953. // This will force a requery for cursor name if reopened.
  954. m_strCursorName.Empty();
  955. if (m_rgFieldInfos != NULL &&
  956. m_nFields > 0 && m_bCheckCacheForDirtyFields)
  957. {
  958. FreeDataCache();
  959. }
  960. FreeRowset();
  961. m_nEditMode = noMode;
  962. delete [] m_rgFieldInfos;
  963. m_rgFieldInfos = NULL;
  964. delete [] m_rgODBCFieldInfos;
  965. m_rgODBCFieldInfos = NULL;
  966. delete [] m_pbFieldFlags;
  967. m_pbFieldFlags = NULL;
  968. delete [] m_pbParamFlags;
  969. m_pbParamFlags = NULL;
  970. if (m_pvFieldProxy != NULL)
  971. {
  972. for (UINT nField = 0; nField < m_nProxyFields; nField++)
  973. delete m_pvFieldProxy[nField];
  974. delete [] m_pvFieldProxy;
  975. m_pvFieldProxy = NULL;
  976. m_nProxyFields = 0;
  977. }
  978. if (m_pvParamProxy != NULL)
  979. {
  980. for (UINT nParam = 0; nParam < m_nProxyParams; nParam++)
  981. delete m_pvParamProxy[nParam];
  982. delete [] m_pvParamProxy;
  983. m_pvParamProxy = NULL;
  984. m_nProxyParams = 0;
  985. }
  986. delete [] m_plParamLength;
  987. m_plParamLength = NULL;
  988. RETCODE nRetCode;
  989. if (m_hstmt != SQL_NULL_HSTMT)
  990. {
  991. AFX_SQL_SYNC(::SQLFreeStmt(m_hstmt, SQL_DROP));
  992. m_hstmt = SQL_NULL_HSTMT;
  993. }
  994. if (m_hstmtUpdate != SQL_NULL_HSTMT)
  995. {
  996. AFX_SQL_SYNC(::SQLFreeStmt(m_hstmtUpdate, SQL_DROP));
  997. m_hstmtUpdate = SQL_NULL_HSTMT;
  998. }
  999. // Remove CRecordset from CDatabase's list
  1000. AfxLockGlobals(CRIT_ODBC);
  1001. TRY
  1002. {
  1003. POSITION pos = m_pDatabase->m_listRecordsets.Find(this);
  1004. if (pos != NULL)
  1005. m_pDatabase->m_listRecordsets.RemoveAt(pos);
  1006. #ifdef _DEBUG
  1007. else if (afxTraceFlags & traceDatabase)
  1008. TRACE0("WARNING: CRecordset not found in m_pDatabase->m_listRecordsets.\n");
  1009. #endif
  1010. }
  1011. CATCH_ALL(e)
  1012. {
  1013. AfxUnlockGlobals(CRIT_ODBC);
  1014. THROW_LAST();
  1015. }
  1016. END_CATCH_ALL
  1017. AfxUnlockGlobals(CRIT_ODBC);
  1018. m_lOpen = AFX_RECORDSET_STATUS_CLOSED;
  1019. m_bBOF = TRUE;
  1020. m_bEOF = TRUE;
  1021. m_bDeleted = FALSE;
  1022. m_bAppendable = FALSE;
  1023. m_bUpdatable = FALSE;
  1024. m_bScrollable = FALSE;
  1025. m_bRebindParams = FALSE;
  1026. m_bLongBinaryColumns = FALSE;
  1027. m_nLockMode = optimistic;
  1028. m_nFieldsBound = 0;
  1029. m_nResultCols = -1;
  1030. }
  1031. BOOL CRecordset::IsOpen() const
  1032. // Note: assumes base class CRecordset::Close called
  1033. {
  1034. if (m_hstmt == NULL)
  1035. return FALSE;
  1036. if (m_lOpen == AFX_RECORDSET_STATUS_OPEN)
  1037. return TRUE;
  1038. RETCODE nRetCode;
  1039. SWORD nCols;
  1040. AFX_ODBC_CALL(::SQLNumResultCols(m_hstmt, &nCols));
  1041. if (!Check(nRetCode))
  1042. {
  1043. // If function sequence error, CRecordset not open
  1044. CDBException* e = new CDBException(nRetCode);
  1045. e->BuildErrorString(m_pDatabase, m_hstmt, FALSE);
  1046. if (e->m_strStateNativeOrigin.Find(_afxOutOfSequence) >= 0)
  1047. {
  1048. e->Delete();
  1049. return FALSE;
  1050. }
  1051. else
  1052. {
  1053. #ifdef _DEBUG
  1054. TRACE0("Error: SQLNumResultCols failed during IsOpen().\n");
  1055. e->TraceErrorMessage(e->m_strError);
  1056. e->TraceErrorMessage(e->m_strStateNativeOrigin);
  1057. #endif
  1058. THROW(e);
  1059. }
  1060. }
  1061. BOOL bOpen = FALSE;
  1062. if (nCols != 0)
  1063. bOpen = TRUE;
  1064. return bOpen;
  1065. }
  1066. BOOL CRecordset::IsFieldDirty(void* pv)
  1067. {
  1068. ASSERT_VALID(this);
  1069. ASSERT(!(m_dwOptions & useMultiRowFetch));
  1070. if (m_nFields <= 0)
  1071. {
  1072. ASSERT(FALSE);
  1073. return FALSE;
  1074. }
  1075. // If not in update op fields can't be dirty
  1076. // must compare saved and current values
  1077. if (m_nEditMode == noMode)
  1078. return FALSE;
  1079. // Must compare values to find dirty fields if necessary
  1080. if (m_bCheckCacheForDirtyFields)
  1081. {
  1082. if (m_nEditMode == edit)
  1083. MarkForUpdate();
  1084. else
  1085. MarkForAddNew();
  1086. }
  1087. int nIndex = 0, nIndexEnd;
  1088. if (pv == NULL)
  1089. nIndexEnd = m_nFields - 1;
  1090. else
  1091. {
  1092. // GetBoundFieldIndex returns 1-based index
  1093. nIndex = nIndexEnd = GetBoundFieldIndex(pv) - 1;
  1094. // must be address of field member
  1095. ASSERT(nIndex >= 0);
  1096. }
  1097. BOOL bDirty = FALSE;
  1098. while (nIndex <= nIndexEnd && !bDirty)
  1099. bDirty = IsFieldStatusDirty(nIndex++);
  1100. return bDirty;
  1101. }
  1102. BOOL CRecordset::IsFieldNull(void* pv)
  1103. {
  1104. ASSERT_VALID(this);
  1105. ASSERT(!(m_dwOptions & useMultiRowFetch));
  1106. int nIndex;
  1107. BOOL bRetVal;
  1108. if (pv == NULL)
  1109. {
  1110. bRetVal = FALSE;
  1111. for (nIndex = 0; !bRetVal && nIndex <= int(m_nFields-1); nIndex++)
  1112. bRetVal = IsFieldStatusNull((DWORD) nIndex);
  1113. }
  1114. else
  1115. {
  1116. nIndex = GetBoundFieldIndex(pv) - 1;
  1117. if (nIndex < 0)
  1118. ThrowDBException(AFX_SQL_ERROR_FIELD_NOT_FOUND);
  1119. bRetVal = IsFieldStatusNull((DWORD) nIndex);
  1120. }
  1121. return bRetVal;
  1122. }
  1123. BOOL CRecordset::IsFieldNullable(void* pv)
  1124. {
  1125. ASSERT_VALID(this);
  1126. if (pv == NULL)
  1127. {
  1128. // Must specify valid column name
  1129. ASSERT(FALSE);
  1130. return FALSE;
  1131. }
  1132. int nIndex = GetBoundFieldIndex(pv) - 1;
  1133. if (nIndex < 0)
  1134. ThrowDBException(AFX_SQL_ERROR_FIELD_NOT_FOUND);
  1135. return IsFieldNullable((DWORD)nIndex);
  1136. }
  1137. BOOL CRecordset::CanBookmark() const
  1138. {
  1139. ASSERT_VALID(this);
  1140. ASSERT(m_pDatabase->IsOpen());
  1141. if (!(m_dwOptions & useBookmarks) ||
  1142. (m_nOpenType == forwardOnly && !(m_dwOptions & useExtendedFetch)))
  1143. return FALSE;
  1144. return m_pDatabase->GetBookmarkPersistence() & SQL_BP_SCROLL;
  1145. }
  1146. void CRecordset::Move(long nRows, WORD wFetchType)
  1147. {
  1148. ASSERT_VALID(this);
  1149. ASSERT(m_hstmt != SQL_NULL_HSTMT);
  1150. // First call - fields haven't been bound (m_nFieldsBound will change)
  1151. if (m_nFieldsBound == 0)
  1152. {
  1153. InitRecord();
  1154. ResetCursor();
  1155. }
  1156. if (m_nFieldsBound > 0)
  1157. {
  1158. // Reset field flags - mark all clean, all non-null
  1159. memset(m_pbFieldFlags, 0, m_nFields);
  1160. // Clear any edit mode that was set
  1161. m_nEditMode = noMode;
  1162. }
  1163. // Check scrollability, EOF/BOF status
  1164. CheckRowsetCurrencyStatus(wFetchType, nRows);
  1165. RETCODE nRetCode;
  1166. // Fetch the data, skipping deleted records if necessary
  1167. if ((wFetchType == SQL_FETCH_FIRST ||
  1168. wFetchType == SQL_FETCH_LAST ||
  1169. wFetchType == SQL_FETCH_NEXT ||
  1170. wFetchType == SQL_FETCH_PRIOR ||
  1171. wFetchType == SQL_FETCH_RELATIVE) &&
  1172. m_dwOptions & skipDeletedRecords)
  1173. {
  1174. SkipDeletedRecords(wFetchType, nRows, &m_dwRowsFetched, &nRetCode);
  1175. }
  1176. else
  1177. // Fetch the data and check for errors
  1178. nRetCode = FetchData(wFetchType, nRows, &m_dwRowsFetched);
  1179. // Set currency status and increment the record counters
  1180. SetRowsetCurrencyStatus(nRetCode, wFetchType, nRows, m_dwRowsFetched);
  1181. // Need to fixup bound fields in some cases
  1182. if (m_nFields > 0 && !IsEOF() && !IsBOF() &&
  1183. !(m_dwOptions & useMultiRowFetch))
  1184. {
  1185. Fixups();
  1186. }
  1187. }
  1188. void CRecordset::CheckRowsetError(RETCODE nRetCode)
  1189. {
  1190. if (nRetCode == SQL_SUCCESS_WITH_INFO)
  1191. {
  1192. CDBException e(nRetCode);
  1193. // Build the error string but don't send nuisance output to TRACE window
  1194. e.BuildErrorString(m_pDatabase, m_hstmt, FALSE);
  1195. if (e.m_strStateNativeOrigin.Find(_afxDataTruncated) >= 0)
  1196. {
  1197. // Ignore data truncated warning if binding long binary columns
  1198. // (may mask non-long binary truncation warnings or other warnings)
  1199. if (!((m_pDatabase->m_dwUpdateOptions & AFX_SQL_SETPOSUPDATES) &&
  1200. m_bLongBinaryColumns))
  1201. {
  1202. NO_CPP_EXCEPTION(e.Empty());
  1203. TRACE0("Error: field data truncated during data fetch.\n");
  1204. ThrowDBException(AFX_SQL_ERROR_DATA_TRUNCATED);
  1205. }
  1206. }
  1207. else if (e.m_strStateNativeOrigin.Find(_afxRowFetch) >= 0)
  1208. {
  1209. #ifdef _DEBUG
  1210. TRACE0("Error: fetching row from server.\n");
  1211. e.TraceErrorMessage(e.m_strError);
  1212. e.TraceErrorMessage(e.m_strStateNativeOrigin);
  1213. #endif
  1214. NO_CPP_EXCEPTION(e.Empty());
  1215. ThrowDBException(AFX_SQL_ERROR_ROW_FETCH);
  1216. }
  1217. else
  1218. {
  1219. #ifdef _DEBUG
  1220. // Not a truncation or row fetch warning so send debug output
  1221. if (afxTraceFlags & traceDatabase)
  1222. {
  1223. TRACE0("Warning: ODBC Success With Info,\n");
  1224. e.TraceErrorMessage(e.m_strError);
  1225. e.TraceErrorMessage(e.m_strStateNativeOrigin);
  1226. }
  1227. #endif // _DEBUG
  1228. }
  1229. }
  1230. else if (!Check(nRetCode))
  1231. ThrowDBException(nRetCode);
  1232. }
  1233. void CRecordset::GetBookmark(CDBVariant& varBookmark)
  1234. {
  1235. ASSERT_VALID(this);
  1236. // Validate bookmarks are usable
  1237. if (!(m_dwOptions & useBookmarks))
  1238. ThrowDBException(AFX_SQL_ERROR_BOOKMARKS_NOT_ENABLED);
  1239. else if (!CanBookmark())
  1240. ThrowDBException(AFX_SQL_ERROR_BOOKMARKS_NOT_SUPPORTED);
  1241. // Currently ODBC only supports 4 byte bookmarks
  1242. // Initialize the variant to a long
  1243. if (varBookmark.m_dwType != DBVT_LONG)
  1244. {
  1245. varBookmark.Clear();
  1246. varBookmark.m_dwType = DBVT_LONG;
  1247. varBookmark.m_lVal = 0;
  1248. }
  1249. RETCODE nRetCode;
  1250. SDWORD nActualSize;
  1251. // Retrieve the bookmark (column 0) data
  1252. AFX_ODBC_CALL(::SQLGetData(m_hstmt, 0, SQL_C_BOOKMARK,
  1253. &varBookmark.m_lVal, sizeof(varBookmark.m_lVal), &nActualSize));
  1254. if (!Check(nRetCode))
  1255. {
  1256. TRACE0("Error: GetBookmark operation failed.\n");
  1257. ThrowDBException(nRetCode);
  1258. }
  1259. }
  1260. void CRecordset::SetBookmark(const CDBVariant& varBookmark)
  1261. {
  1262. ASSERT_VALID(this);
  1263. // Validate bookmarks are usable
  1264. if (!(m_dwOptions & useBookmarks))
  1265. ThrowDBException(AFX_SQL_ERROR_BOOKMARKS_NOT_ENABLED);
  1266. else if (!CanBookmark())
  1267. ThrowDBException(AFX_SQL_ERROR_BOOKMARKS_NOT_SUPPORTED);
  1268. // Currently ODBC only supports 4 byte bookmarks
  1269. ASSERT(varBookmark.m_dwType == DBVT_LONG);
  1270. Move(varBookmark.m_lVal, SQL_FETCH_BOOKMARK);
  1271. }
  1272. void CRecordset::SetRowsetSize(DWORD dwNewRowsetSize)
  1273. {
  1274. ASSERT_VALID(this);
  1275. ASSERT(dwNewRowsetSize > 0);
  1276. // If not yet open, only set expected length
  1277. if (!IsOpen())
  1278. {
  1279. m_dwRowsetSize = dwNewRowsetSize;
  1280. return;
  1281. }
  1282. if (!(m_dwOptions & useMultiRowFetch))
  1283. {
  1284. // Only works if bulk row fetching!
  1285. ASSERT(FALSE);
  1286. return;
  1287. }
  1288. // Need to reallocate some memory if rowset size grows
  1289. if (m_dwAllocatedRowsetSize == 0 ||
  1290. (m_dwAllocatedRowsetSize < dwNewRowsetSize))
  1291. {
  1292. // If rowset already allocated, delete old and reallocate
  1293. FreeRowset();
  1294. m_rgRowStatus = new WORD[dwNewRowsetSize];
  1295. // If not a user allocated buffer grow the data buffers
  1296. if (!(m_dwOptions & userAllocMultiRowBuffers))
  1297. {
  1298. // Allocate the rowset field buffers
  1299. m_dwRowsetSize = dwNewRowsetSize;
  1300. CFieldExchange fx(CFieldExchange::AllocMultiRowBuffer, this);
  1301. DoBulkFieldExchange(&fx);
  1302. m_dwAllocatedRowsetSize = dwNewRowsetSize;
  1303. // Set bound fields to zero, rebind and reset bound field count
  1304. int nOldFieldsBound = m_nFieldsBound;
  1305. m_nFieldsBound = 0;
  1306. InitRecord();
  1307. m_nFieldsBound = nOldFieldsBound;
  1308. }
  1309. }
  1310. else
  1311. {
  1312. // Just reset the new rowset size
  1313. m_dwRowsetSize = dwNewRowsetSize;
  1314. }
  1315. RETCODE nRetCode;
  1316. AFX_SQL_SYNC(::SQLSetStmtOption(m_hstmt, SQL_ROWSET_SIZE,
  1317. m_dwRowsetSize));
  1318. }
  1319. void CRecordset::AddNew()
  1320. {
  1321. ASSERT_VALID(this);
  1322. ASSERT(m_hstmt != SQL_NULL_HSTMT);
  1323. // we can't construct an INSERT statement w/o any columns
  1324. ASSERT(m_nFields != 0);
  1325. if (!m_bAppendable)
  1326. {
  1327. ThrowDBException(AFX_SQL_ERROR_RECORDSET_READONLY);
  1328. }
  1329. if (m_dwOptions & useMultiRowFetch)
  1330. {
  1331. // Can't use update methods on multi-row rowset
  1332. ASSERT(FALSE);
  1333. return;
  1334. }
  1335. if (m_bCheckCacheForDirtyFields && m_nFields > 0)
  1336. {
  1337. if (m_nEditMode == noMode)
  1338. {
  1339. // First addnew call, cache record values
  1340. StoreFields();
  1341. }
  1342. else
  1343. {
  1344. // subsequent Edit/AddNew call. Restore values, save them again
  1345. LoadFields();
  1346. StoreFields();
  1347. }
  1348. }
  1349. SetFieldNull(NULL);
  1350. SetFieldDirty(NULL, FALSE);
  1351. m_nEditMode = addnew;
  1352. }
  1353. void CRecordset::Edit()
  1354. {
  1355. ASSERT_VALID(this);
  1356. ASSERT(m_hstmt != SQL_NULL_HSTMT);
  1357. // we can't construct an UPDATE statement w/o any columns
  1358. ASSERT(m_nFields != 0);
  1359. if (!m_bUpdatable)
  1360. ThrowDBException(AFX_SQL_ERROR_RECORDSET_READONLY);
  1361. if (m_dwOptions & useMultiRowFetch)
  1362. {
  1363. // Can't use update methods on multi-row rowset
  1364. ASSERT(FALSE);
  1365. return;
  1366. }
  1367. if (m_bEOF || m_bBOF || m_bDeleted)
  1368. {
  1369. TRACE0("Error: Edit attempt failed - not on a record.\n");
  1370. ThrowDBException(AFX_SQL_ERROR_NO_CURRENT_RECORD);
  1371. }
  1372. if ((m_nOpenType == dynaset || m_nOpenType == dynamic) &&
  1373. m_nLockMode == pessimistic)
  1374. {
  1375. RETCODE nRetCode;
  1376. AFX_ODBC_CALL(::SQLSetPos(m_hstmt, 1, SQL_POSITION,
  1377. SQL_LCK_EXCLUSIVE));
  1378. if (!Check(nRetCode))
  1379. {
  1380. TRACE0("Error: attempt to lock record failed during Edit function.\n");
  1381. ThrowDBException(nRetCode);
  1382. }
  1383. }
  1384. if (m_bCheckCacheForDirtyFields && m_nFields > 0)
  1385. {
  1386. if (m_nEditMode == noMode)
  1387. // First edit call, cache record values
  1388. StoreFields();
  1389. else
  1390. {
  1391. // subsequent Edit/AddNew call. Restore values, save them again
  1392. LoadFields();
  1393. StoreFields();
  1394. }
  1395. }
  1396. m_nEditMode = edit;
  1397. }
  1398. BOOL CRecordset::Update()
  1399. {
  1400. ASSERT_VALID(this);
  1401. ASSERT(m_hstmt != SQL_NULL_HSTMT);
  1402. if (m_dwOptions & useMultiRowFetch)
  1403. {
  1404. // Can't use update methods on multi-row rowset
  1405. ASSERT(FALSE);
  1406. return FALSE;
  1407. }
  1408. if (m_nEditMode != addnew && m_nEditMode != edit)
  1409. {
  1410. TRACE0("Error: must enter Edit or AddNew mode before updating.\n");
  1411. ThrowDBException(AFX_SQL_ERROR_ILLEGAL_MODE);
  1412. }
  1413. return UpdateInsertDelete();
  1414. }
  1415. void CRecordset::Delete()
  1416. {
  1417. ASSERT_VALID(this);
  1418. ASSERT(m_hstmt != SQL_NULL_HSTMT);
  1419. if (m_dwOptions & useMultiRowFetch)
  1420. {
  1421. // Can't use update methods on multi-row rowset
  1422. ASSERT(FALSE);
  1423. return;
  1424. }
  1425. if (m_nEditMode != noMode)
  1426. {
  1427. TRACE0("Error: attempted to delete while still in Edit or AddNew mode.\n");
  1428. ThrowDBException(AFX_SQL_ERROR_ILLEGAL_MODE);
  1429. }
  1430. UpdateInsertDelete(); // This call can't fail in delete mode (noMode)
  1431. }
  1432. void CRecordset::CancelUpdate()
  1433. {
  1434. ASSERT_VALID(this);
  1435. ASSERT(IsOpen());
  1436. if (m_nEditMode == noMode)
  1437. // Do nothing if not in edit mode
  1438. return;
  1439. else
  1440. // Reset the edit mode
  1441. m_nEditMode = noMode;
  1442. // Restore cache if necessary
  1443. if (m_bCheckCacheForDirtyFields && m_nFields > 0)
  1444. LoadFields();
  1445. }
  1446. BOOL CRecordset::FlushResultSet() const
  1447. {
  1448. RETCODE nRetCode;
  1449. AFX_ODBC_CALL(::SQLMoreResults(m_hstmt));
  1450. if (!Check(nRetCode))
  1451. {
  1452. TRACE0("Error: attempt FlushResultSet failed.\n");
  1453. AfxThrowDBException(nRetCode, m_pDatabase, m_hstmt);
  1454. }
  1455. // Reset state of cursor
  1456. ((CRecordset*)this)->ResetCursor();
  1457. return nRetCode != SQL_NO_DATA_FOUND;
  1458. }
  1459. void CRecordset::GetODBCFieldInfo(LPCTSTR lpszName,
  1460. CODBCFieldInfo& fieldinfo)
  1461. {
  1462. ASSERT_VALID(this);
  1463. ASSERT(IsOpen());
  1464. ASSERT(lpszName != NULL);
  1465. // No data or no column info fetched yet
  1466. if (GetODBCFieldCount() <= 0)
  1467. {
  1468. ASSERT(FALSE);
  1469. return;
  1470. }
  1471. // Get the index of the field corresponding to name
  1472. short nField = GetFieldIndexByName(lpszName);
  1473. GetODBCFieldInfo(nField, fieldinfo);
  1474. }
  1475. void CRecordset::GetODBCFieldInfo(short nIndex,
  1476. CODBCFieldInfo& fieldinfo)
  1477. {
  1478. ASSERT_VALID(this);
  1479. ASSERT(IsOpen());
  1480. // No data or no column info fetched yet
  1481. if (GetODBCFieldCount() <= 0)
  1482. {
  1483. ASSERT(FALSE);
  1484. return;
  1485. }
  1486. // Just copy the data into the field info
  1487. CODBCFieldInfo* pInfo = &m_rgODBCFieldInfos[nIndex];
  1488. fieldinfo.m_strName = pInfo->m_strName;
  1489. fieldinfo.m_nSQLType = pInfo->m_nSQLType;
  1490. fieldinfo.m_nPrecision = pInfo->m_nPrecision;
  1491. fieldinfo.m_nScale = pInfo->m_nScale;
  1492. fieldinfo.m_nNullability = pInfo->m_nNullability;
  1493. }
  1494. void CRecordset::GetFieldValue(LPCTSTR lpszName,
  1495. CDBVariant& varValue, short nFieldType)
  1496. {
  1497. ASSERT_VALID(this);
  1498. ASSERT(IsOpen());
  1499. ASSERT(lpszName != NULL);
  1500. // No data or no column info fetched yet
  1501. if (GetODBCFieldCount() <= 0)
  1502. {
  1503. ASSERT(FALSE);
  1504. varValue.Clear();
  1505. return;
  1506. }
  1507. // Get the index of the field corresponding to name
  1508. short nField = GetFieldIndexByName(lpszName);
  1509. GetFieldValue(nField, varValue, nFieldType);
  1510. }
  1511. void CRecordset::GetFieldValue(short nIndex,
  1512. CDBVariant& varValue, short nFieldType)
  1513. {
  1514. ASSERT_VALID(this);
  1515. ASSERT(IsOpen());
  1516. // Clear the previous variant
  1517. varValue.Clear();
  1518. // No data or no column info fetched yet
  1519. if (GetODBCFieldCount() <= 0)
  1520. {
  1521. ASSERT(FALSE);
  1522. return;
  1523. }
  1524. // Convert index to 1-based and check range
  1525. nIndex++;
  1526. if (nIndex < 1 || nIndex > GetODBCFieldCount())
  1527. {
  1528. ThrowDBException(AFX_SQL_ERROR_FIELD_NOT_FOUND);
  1529. }
  1530. void* pvData = NULL;
  1531. int nLen = 0;
  1532. // Determine the default field type and get the data buffer
  1533. if (nFieldType == DEFAULT_FIELD_TYPE)
  1534. {
  1535. nFieldType =
  1536. GetDefaultFieldType(m_rgODBCFieldInfos[nIndex - 1].m_nSQLType);
  1537. }
  1538. pvData = GetDataBuffer(varValue, nFieldType, &nLen,
  1539. m_rgODBCFieldInfos[nIndex - 1].m_nSQLType,
  1540. m_rgODBCFieldInfos[nIndex - 1].m_nPrecision);
  1541. // Now can actually get the data
  1542. long nActualSize = GetData(m_pDatabase, m_hstmt, nIndex,
  1543. nFieldType, pvData, nLen,
  1544. m_rgODBCFieldInfos[nIndex - 1].m_nSQLType);
  1545. // Handle NULL data separately
  1546. if (nActualSize == SQL_NULL_DATA)
  1547. {
  1548. // Clear value and set the value NULL
  1549. varValue.Clear();
  1550. }
  1551. else
  1552. {
  1553. // May need to cleanup and call SQLGetData again if LONG_VAR data
  1554. if (nFieldType == SQL_C_CHAR)
  1555. {
  1556. GetLongCharDataAndCleanup(m_pDatabase, m_hstmt, nIndex,
  1557. nActualSize, &pvData, nLen, *varValue.m_pstring,
  1558. m_rgODBCFieldInfos[nIndex - 1].m_nSQLType);
  1559. #ifdef _UNICODE
  1560. // Now must convert string to UNICODE
  1561. LPCSTR lpszOld = (LPCSTR)varValue.m_pstring->GetBuffer(0);
  1562. CString* pStringNew = new CString(lpszOld);
  1563. delete varValue.m_pstring;
  1564. varValue.m_pstring = pStringNew;
  1565. #endif // _UNICODE
  1566. }
  1567. else if (nFieldType == SQL_C_BINARY)
  1568. {
  1569. GetLongBinaryDataAndCleanup(m_pDatabase, m_hstmt, nIndex,
  1570. nActualSize, &pvData, nLen, varValue,
  1571. m_rgODBCFieldInfos[nIndex - 1].m_nSQLType);
  1572. }
  1573. }
  1574. }
  1575. void CRecordset::GetFieldValue(LPCTSTR lpszName, CString& strValue)
  1576. {
  1577. ASSERT_VALID(this);
  1578. ASSERT(IsOpen());
  1579. ASSERT(lpszName != NULL);
  1580. // No data or no column info fetched yet
  1581. if (GetODBCFieldCount() <= 0)
  1582. {
  1583. ASSERT(FALSE);
  1584. return;
  1585. }
  1586. // Get the index of the field corresponding to name
  1587. short nField = GetFieldIndexByName(lpszName);
  1588. GetFieldValue(nField, strValue);
  1589. }
  1590. void CRecordset::GetFieldValue(short nIndex, CString& strValue)
  1591. {
  1592. ASSERT_VALID(this);
  1593. ASSERT(IsOpen());
  1594. // No data or no column info fetched yet
  1595. if (GetODBCFieldCount() <= 0)
  1596. {
  1597. ASSERT(FALSE);
  1598. return;
  1599. }
  1600. // Convert index to 1-based and check range
  1601. nIndex++;
  1602. if (nIndex < 1 || nIndex > GetODBCFieldCount())
  1603. {
  1604. ThrowDBException(AFX_SQL_ERROR_FIELD_NOT_FOUND);
  1605. }
  1606. int nLen = GetTextLen(m_rgODBCFieldInfos[nIndex - 1].m_nSQLType,
  1607. m_rgODBCFieldInfos[nIndex - 1].m_nPrecision);
  1608. #ifndef _UNICODE
  1609. CString& strData = strValue;
  1610. #else
  1611. CString strProxy;
  1612. CString& strData = strProxy;
  1613. #endif
  1614. void* pvData = strData.GetBufferSetLength(nLen);
  1615. // Now can actually get the data
  1616. long nActualSize = GetData(m_pDatabase, m_hstmt, nIndex,
  1617. SQL_C_CHAR, pvData, nLen,
  1618. m_rgODBCFieldInfos[nIndex - 1].m_nSQLType);
  1619. // Handle NULL data separately
  1620. if (nActualSize == SQL_NULL_DATA)
  1621. {
  1622. // Clear value
  1623. strValue.Empty();
  1624. }
  1625. else
  1626. {
  1627. // May need to cleanup and call SQLGetData again if necessary
  1628. GetLongCharDataAndCleanup(m_pDatabase, m_hstmt, nIndex,
  1629. nActualSize, &pvData, nLen, strData,
  1630. m_rgODBCFieldInfos[nIndex - 1].m_nSQLType);
  1631. #ifdef _UNICODE
  1632. // Now must convert string to UNICODE
  1633. strValue = (LPCSTR)strData.GetBuffer(0);
  1634. #endif // _UNIOCDE
  1635. }
  1636. }
  1637. void CRecordset::SetFieldDirty(void* pv, BOOL bDirty)
  1638. {
  1639. ASSERT_VALID(this);
  1640. int nIndex, nIndexEnd;
  1641. // If not setting all NULL, check simple case
  1642. if (pv != NULL)
  1643. {
  1644. // GetBoundFieldIndex returns 1-based index
  1645. nIndex = GetBoundFieldIndex(pv) - 1;
  1646. if (nIndex < 0)
  1647. {
  1648. // pv must be address of field member
  1649. ASSERT(FALSE);
  1650. return;
  1651. }
  1652. else
  1653. {
  1654. nIndexEnd = nIndex;
  1655. }
  1656. }
  1657. else
  1658. {
  1659. nIndex = 0;
  1660. nIndexEnd = m_nFields - 1;
  1661. }
  1662. while (nIndex <= nIndexEnd)
  1663. {
  1664. if (bDirty)
  1665. SetDirtyFieldStatus((DWORD)nIndex);
  1666. else
  1667. ClearDirtyFieldStatus((DWORD)nIndex);
  1668. nIndex++;
  1669. }
  1670. }
  1671. void CRecordset::SetFieldNull(void* pv, BOOL bNull)
  1672. {
  1673. ASSERT_VALID(this);
  1674. ASSERT(IsOpen());
  1675. ASSERT(!(m_dwOptions & useMultiRowFetch));
  1676. // If not setting all fields NULL, check simple case (param) first
  1677. if (pv != NULL)
  1678. {
  1679. // Cached index is 1-based
  1680. int nIndex = GetBoundParamIndex(pv) - 1;
  1681. if (nIndex >= 0)
  1682. {
  1683. if (bNull)
  1684. SetNullParamStatus(nIndex);
  1685. else
  1686. ClearNullParamStatus(nIndex);
  1687. return;
  1688. }
  1689. }
  1690. // Not a param, must be a field
  1691. if (m_nFields <= 0)
  1692. {
  1693. ASSERT(FALSE);
  1694. return;
  1695. }
  1696. // Need field exchange mechanism to set PSEUDO NULL values
  1697. // and to reset data lengths (especially for RFX_LongBinary)
  1698. CFieldExchange fx(CFieldExchange::SetFieldNull, this, pv);
  1699. fx.m_nFieldFound = 0;
  1700. fx.m_bField = bNull;
  1701. DoFieldExchange(&fx);
  1702. // If no field found, m_nFieldFound will still be zero
  1703. ASSERT(fx.m_nFieldFound != 0);
  1704. }
  1705. void CRecordset::SetParamNull(int nIndex, BOOL bNull)
  1706. {
  1707. ASSERT_VALID(this);
  1708. ASSERT((DWORD)nIndex < m_nParams);
  1709. // Can be called before Open, but need to alloc status arrays first
  1710. if (!IsOpen())
  1711. AllocStatusArrays();
  1712. if (bNull)
  1713. SetNullParamStatus(nIndex);
  1714. else
  1715. ClearNullParamStatus(nIndex);
  1716. return;
  1717. }
  1718. void CRecordset::SetLockingMode(UINT nLockMode)
  1719. {
  1720. if (nLockMode == pessimistic)
  1721. {
  1722. RETCODE nRetCode;
  1723. UDWORD dwTypes;
  1724. SWORD nResult;
  1725. AFX_SQL_SYNC(::SQLGetInfo(m_pDatabase->m_hdbc, SQL_LOCK_TYPES,
  1726. &dwTypes, sizeof(dwTypes), &nResult));
  1727. if (!Check(nRetCode) || !(dwTypes & SQL_LCK_EXCLUSIVE))
  1728. ThrowDBException(AFX_SQL_ERROR_LOCK_MODE_NOT_SUPPORTED);
  1729. }
  1730. m_nLockMode = nLockMode;
  1731. }
  1732. BOOL CRecordset::Requery()
  1733. {
  1734. RETCODE nRetCode;
  1735. ASSERT_VALID(this);
  1736. ASSERT(IsOpen());
  1737. // Can't requery if using direct execution
  1738. if (m_dwOptions & executeDirect)
  1739. return FALSE;
  1740. TRY
  1741. {
  1742. // Detect changes to filter and sort
  1743. if ((m_strFilter != m_strRequeryFilter) || (m_strSort != m_strRequerySort))
  1744. {
  1745. m_strRequeryFilter = m_strFilter;
  1746. m_strRequerySort = m_strSort;
  1747. Close();
  1748. if (m_strRequerySQL.IsEmpty())
  1749. return Open(m_nOpenType, NULL, m_dwOptions);
  1750. else
  1751. return Open(m_nOpenType, m_strRequerySQL, m_dwOptions);
  1752. }
  1753. else
  1754. {
  1755. // Shutdown current query, preserving buffers for performance
  1756. AFX_SQL_SYNC(::SQLFreeStmt(m_hstmt, SQL_CLOSE));
  1757. m_lOpen = AFX_RECORDSET_STATUS_CLOSED;
  1758. // Rebind date/time parameters
  1759. RebindParams(m_hstmt);
  1760. // now attempt to re-execute the SQL Query
  1761. AFX_ODBC_CALL(::SQLExecute(m_hstmt));
  1762. if (!Check(nRetCode))
  1763. {
  1764. TRACE0("Error: Requery attempt failed.\n");
  1765. ThrowDBException(nRetCode);
  1766. }
  1767. m_lOpen = AFX_RECORDSET_STATUS_OPEN;
  1768. // Reset some cursor properties and fetch first record
  1769. ResetCursor();
  1770. MoveNext();
  1771. // If EOF, then result set empty, so set BOF as well
  1772. m_bBOF = m_bEOF;
  1773. }
  1774. }
  1775. CATCH_ALL(e)
  1776. {
  1777. Close();
  1778. THROW_LAST();
  1779. }
  1780. END_CATCH_ALL
  1781. return TRUE; // all set
  1782. }
  1783. // Shutdown any pending query for CRecordset's hstmt's
  1784. void CRecordset::Cancel()
  1785. {
  1786. ASSERT_VALID(this);
  1787. ASSERT(m_hstmt != SQL_NULL_HSTMT);
  1788. ::SQLCancel(m_hstmt);
  1789. // If Update hstmt has been allocated, shut it down also
  1790. if (m_hstmtUpdate != SQL_NULL_HSTMT)
  1791. ::SQLCancel(m_hstmtUpdate);
  1792. }
  1793. CString CRecordset::GetDefaultConnect()
  1794. {
  1795. ASSERT_VALID(this);
  1796. return _afxODBCTrail;
  1797. }
  1798. CString CRecordset::GetDefaultSQL()
  1799. {
  1800. ASSERT_VALID(this);
  1801. // Override and add table name or entire SQL SELECT statement
  1802. return _T("");
  1803. }
  1804. void CRecordset::DoFieldExchange(CFieldExchange* /* pFX */)
  1805. {
  1806. ASSERT_VALID(this);
  1807. // Do nothing if dynamically retrieving unbound fields,
  1808. // otherwise override CRecordset and add RFX calls
  1809. }
  1810. void CRecordset::DoBulkFieldExchange(CFieldExchange* /* pFX */)
  1811. {
  1812. ASSERT_VALID(this);
  1813. // To use multi-record data fetching, you must use
  1814. // a derived CRecordset class and call Close explicitly.
  1815. ASSERT(FALSE);
  1816. }
  1817. void CRecordset::OnSetOptions(HSTMT hstmt)
  1818. {
  1819. ASSERT_VALID(this);
  1820. ASSERT(hstmt != SQL_NULL_HSTMT);
  1821. // Inherit options settings from CDatabase
  1822. m_pDatabase->OnSetOptions(hstmt);
  1823. // If fowardOnly recordset and not using SQLExtendedFetch, quit now
  1824. if (m_nOpenType == forwardOnly && !(m_dwOptions & useExtendedFetch))
  1825. return;
  1826. // Turn on bookmark support if necessary
  1827. EnableBookmarks();
  1828. // If using forwardOnly and extended fetch, quit now
  1829. if (m_nOpenType == forwardOnly)
  1830. return;
  1831. // Make sure driver supports extended fetch, ODBC 2.0 and requested cursor type
  1832. VerifyDriverBehavior();
  1833. DWORD dwScrollType = VerifyCursorSupport();
  1834. // Set the update method, concurrency and cursor type
  1835. SetUpdateMethod();
  1836. SetConcurrencyAndCursorType(hstmt, dwScrollType);
  1837. }
  1838. // Screen for errors.
  1839. BOOL CRecordset::Check(RETCODE nRetCode) const
  1840. {
  1841. ASSERT_VALID(this);
  1842. switch (nRetCode)
  1843. {
  1844. case SQL_SUCCESS_WITH_INFO:
  1845. #ifdef _DEBUG
  1846. if (afxTraceFlags & traceDatabase)
  1847. {
  1848. CDBException e(nRetCode);
  1849. TRACE0("Warning: ODBC Success With Info, ");
  1850. e.BuildErrorString(m_pDatabase, m_hstmt);
  1851. }
  1852. #endif
  1853. // Fall through
  1854. case SQL_SUCCESS:
  1855. case SQL_NO_DATA_FOUND:
  1856. case SQL_NEED_DATA:
  1857. return TRUE;
  1858. }
  1859. return FALSE;
  1860. }
  1861. void CRecordset::PreBindFields()
  1862. {
  1863. // Do nothing
  1864. }
  1865. //////////////////////////////////////////////////////////////////////////////
  1866. // CRecordset internal functions
  1867. // Cache state information internally in CRecordset
  1868. void CRecordset::SetState(int nOpenType, LPCTSTR lpszSQL, DWORD dwOptions)
  1869. {
  1870. if (nOpenType == AFX_DB_USE_DEFAULT_TYPE)
  1871. m_nOpenType = m_nDefaultType;
  1872. else
  1873. m_nOpenType = nOpenType;
  1874. m_bAppendable = (dwOptions & appendOnly) != 0 ||
  1875. (dwOptions & readOnly) == 0;
  1876. m_bUpdatable = (dwOptions & readOnly) == 0 &&
  1877. (dwOptions & appendOnly) == 0;
  1878. // Can turn off dirty field checking via dwOptions
  1879. if (dwOptions & noDirtyFieldCheck || dwOptions & useMultiRowFetch)
  1880. m_bCheckCacheForDirtyFields = FALSE;
  1881. // Set recordset readOnly if forwardOnly
  1882. if (m_nOpenType == forwardOnly && !(dwOptions & readOnly))
  1883. {
  1884. #ifdef _DEBUG
  1885. if (afxTraceFlags & traceDatabase)
  1886. TRACE0("Warning: Setting forwardOnly recordset readOnly.\n");
  1887. #endif
  1888. dwOptions |= readOnly;
  1889. // If using multiRowFetch also set useExtendFetch
  1890. if (dwOptions & useMultiRowFetch)
  1891. dwOptions |= useExtendedFetch;
  1892. }
  1893. // Archive info for use in Requery
  1894. m_dwOptions = dwOptions;
  1895. m_strRequerySQL = lpszSQL;
  1896. m_strRequeryFilter = m_strFilter;
  1897. m_strRequerySort = m_strSort;
  1898. }
  1899. // Allocate the Hstmt and implicitly create and open Database if necessary
  1900. BOOL CRecordset::AllocHstmt()
  1901. {
  1902. RETCODE nRetCode;
  1903. if (m_hstmt == SQL_NULL_HSTMT)
  1904. {
  1905. CString strDefaultConnect;
  1906. TRY
  1907. {
  1908. if (m_pDatabase == NULL)
  1909. {
  1910. m_pDatabase = new CDatabase();
  1911. m_bRecordsetDb = TRUE;
  1912. }
  1913. strDefaultConnect = GetDefaultConnect();
  1914. // If not already opened, attempt to open
  1915. if (!m_pDatabase->IsOpen())
  1916. {
  1917. BOOL bUseCursorLib = m_bUseODBCCursorLib;
  1918. // If non-readOnly snapshot request must use cursor lib
  1919. if (m_nOpenType == snapshot && !(m_dwOptions & readOnly))
  1920. {
  1921. // This assumes drivers only support readOnly snapshots
  1922. bUseCursorLib = TRUE;
  1923. }
  1924. if (!m_pDatabase->Open(_T(""), FALSE, FALSE,
  1925. strDefaultConnect, bUseCursorLib))
  1926. {
  1927. return FALSE;
  1928. }
  1929. // If snapshot cursor requested and not supported, load cursor lib
  1930. if (m_nOpenType == snapshot && !bUseCursorLib)
  1931. {
  1932. // Get the supported cursor types
  1933. RETCODE nResult;
  1934. UDWORD dwDriverScrollOptions;
  1935. AFX_SQL_SYNC(::SQLGetInfo(m_pDatabase->m_hdbc, SQL_SCROLL_OPTIONS,
  1936. &dwDriverScrollOptions, sizeof(dwDriverScrollOptions), &nResult));
  1937. if (!Check(nRetCode))
  1938. {
  1939. TRACE0("Error: ODBC failure checking for driver capabilities.\n");
  1940. ThrowDBException(nRetCode);
  1941. }
  1942. // Check for STATIC cursor support and load cursor lib
  1943. if (!(dwDriverScrollOptions & SQL_SO_STATIC))
  1944. {
  1945. m_pDatabase->Close();
  1946. if (!m_pDatabase->Open(_T(""), FALSE, FALSE,
  1947. strDefaultConnect, TRUE))
  1948. return FALSE;
  1949. }
  1950. }
  1951. }
  1952. AFX_SQL_SYNC(::SQLAllocStmt(m_pDatabase->m_hdbc, &m_hstmt));
  1953. if (!Check(nRetCode))
  1954. ThrowDBException(SQL_INVALID_HANDLE);
  1955. // Add to list of CRecordsets with alloced hstmts
  1956. AfxLockGlobals(CRIT_ODBC);
  1957. TRY
  1958. {
  1959. m_pDatabase->m_listRecordsets.AddHead(this);
  1960. }
  1961. CATCH_ALL(e)
  1962. {
  1963. AfxUnlockGlobals(CRIT_ODBC);
  1964. THROW_LAST();
  1965. }
  1966. END_CATCH_ALL
  1967. AfxUnlockGlobals(CRIT_ODBC);
  1968. }
  1969. CATCH_ALL(e)
  1970. {
  1971. #ifdef _DEBUG
  1972. if (afxTraceFlags & traceDatabase)
  1973. TRACE0("Error: CDatabase create for CRecordset failed.\n");
  1974. #endif
  1975. NO_CPP_EXCEPTION(strDefaultConnect.Empty());
  1976. if (m_bRecordsetDb)
  1977. {
  1978. delete m_pDatabase;
  1979. m_pDatabase = NULL;
  1980. }
  1981. ASSERT(m_hstmt == SQL_NULL_HSTMT);
  1982. THROW_LAST();
  1983. }
  1984. END_CATCH_ALL
  1985. }
  1986. return TRUE;
  1987. }
  1988. // Initialize the status arrays and create the SQL
  1989. void CRecordset::BuildSQL(LPCTSTR lpszSQL)
  1990. {
  1991. if (lpszSQL == NULL)
  1992. m_strSQL = GetDefaultSQL();
  1993. else
  1994. m_strSQL = lpszSQL;
  1995. // Set any supplied params
  1996. if (m_nParams != 0)
  1997. {
  1998. UINT nParams = BindParams(m_hstmt);
  1999. ASSERT(nParams == m_nParams);
  2000. }
  2001. // Construct the SQL string
  2002. BuildSelectSQL();
  2003. AppendFilterAndSortSQL();
  2004. // Do some extra checking if trying to set recordset updatable or appendable
  2005. if ((m_bUpdatable || m_bAppendable) && !IsRecordsetUpdatable())
  2006. m_bUpdatable = m_bAppendable = FALSE;
  2007. if (m_bUpdatable && m_bUseUpdateSQL && m_pDatabase->m_bAddForUpdate)
  2008. m_strSQL += _afxForUpdate;
  2009. // Replace brackets with SQL_IDENTIFIER_QUOTE_CHAR
  2010. m_pDatabase->ReplaceBrackets(m_strSQL.GetBuffer(0));
  2011. m_strSQL.ReleaseBuffer();
  2012. }
  2013. // Prepare and Execute the SQL or simple call SQLExecDirect, resetting concurrency if necessary
  2014. void CRecordset::PrepareAndExecute()
  2015. {
  2016. USES_CONVERSION;
  2017. RETCODE nRetCode = 0;
  2018. BOOL bConcurrency = FALSE;
  2019. LPCSTR lpszWSQL = T2CA(m_strSQL);
  2020. while (!bConcurrency)
  2021. {
  2022. // Prepare or execute the query
  2023. if (m_dwOptions & executeDirect)
  2024. {
  2025. AFX_ODBC_CALL(::SQLExecDirect(m_hstmt,
  2026. (UCHAR*)lpszWSQL, SQL_NTS));
  2027. }
  2028. else
  2029. {
  2030. AFX_ODBC_CALL(::SQLPrepare(m_hstmt,
  2031. (UCHAR*)lpszWSQL, SQL_NTS));
  2032. }
  2033. if (Check(nRetCode))
  2034. bConcurrency = TRUE;
  2035. else
  2036. {
  2037. // If "Driver Not Capable" error, assume cursor type doesn't
  2038. // support requested concurrency and try alternate concurrency.
  2039. CDBException* e = new CDBException(nRetCode);
  2040. e->BuildErrorString(m_pDatabase, m_hstmt);
  2041. if (m_dwConcurrency != SQL_CONCUR_READ_ONLY &&
  2042. e->m_strStateNativeOrigin.Find(_afxDriverNotCapable) >= 0)
  2043. {
  2044. #ifdef _DEBUG
  2045. if (afxTraceFlags & traceDatabase)
  2046. TRACE0("Warning: Driver does not support requested concurrency.\n");
  2047. #endif
  2048. // Don't need exception to persist while attempting to reset concurrency
  2049. e->Delete();
  2050. // ODBC will automatically attempt to set alternate concurrency if
  2051. // request fails, but it won't try LOCK even if driver supports it.
  2052. if ((m_dwDriverConcurrency & SQL_SCCO_LOCK) &&
  2053. (m_dwConcurrency == SQL_CONCUR_ROWVER ||
  2054. m_dwConcurrency == SQL_CONCUR_VALUES))
  2055. {
  2056. m_dwConcurrency = SQL_CONCUR_LOCK;
  2057. }
  2058. else
  2059. {
  2060. m_dwConcurrency = SQL_CONCUR_READ_ONLY;
  2061. m_bUpdatable = m_bAppendable = FALSE;
  2062. #ifdef _DEBUG
  2063. if (afxTraceFlags & traceDatabase)
  2064. TRACE0("Warning: Setting recordset read only.\n");
  2065. #endif
  2066. }
  2067. // Attempt to reset the concurrency model.
  2068. AFX_SQL_SYNC(::SQLSetStmtOption(m_hstmt, SQL_CONCURRENCY,
  2069. m_dwConcurrency));
  2070. if (!Check(nRetCode))
  2071. {
  2072. TRACE0("Error: ODBC failure setting recordset concurrency.\n");
  2073. ThrowDBException(nRetCode);
  2074. }
  2075. }
  2076. else
  2077. {
  2078. TRACE0("Error: ODBC failure on SQLPrepare or SQLExecDirect\n");
  2079. THROW(e);
  2080. }
  2081. }
  2082. }
  2083. // now attempt to execute the SQL Query if not executed already
  2084. if (!(m_dwOptions & executeDirect))
  2085. {
  2086. AFX_ODBC_CALL(::SQLExecute(m_hstmt));
  2087. if (!Check(nRetCode))
  2088. ThrowDBException(nRetCode);
  2089. }
  2090. m_lOpen = AFX_RECORDSET_STATUS_OPEN;
  2091. // SQLExecute or SQLExecDirect may have changed an option value
  2092. if (nRetCode == SQL_SUCCESS_WITH_INFO)
  2093. {
  2094. // Check if concurrency was changed in order to mark
  2095. // recordset non-updatable if necessary
  2096. DWORD dwConcurrency;
  2097. AFX_SQL_SYNC(::SQLGetStmtOption(m_hstmt, SQL_CONCURRENCY, &dwConcurrency));
  2098. if (!Check(nRetCode))
  2099. ThrowDBException(nRetCode);
  2100. if (dwConcurrency == SQL_CONCUR_READ_ONLY && (m_bUpdatable || m_bAppendable))
  2101. {
  2102. m_bUpdatable = FALSE;
  2103. m_bAppendable = FALSE;
  2104. #ifdef _DEBUG
  2105. if (afxTraceFlags & traceDatabase)
  2106. {
  2107. TRACE0("Warning: Concurrency changed by driver.\n");
  2108. TRACE0("\tMarking CRecordset as not updatable.\n");
  2109. }
  2110. #endif // _DEBUG
  2111. }
  2112. }
  2113. }
  2114. // Ensure that driver supports extended fetch and ODBC 2.0 if necessary
  2115. void CRecordset::VerifyDriverBehavior()
  2116. {
  2117. RETCODE nRetCode;
  2118. UWORD wScrollable;
  2119. // If SQLExtendedFetch not supported, use SQLFetch
  2120. AFX_SQL_SYNC(::SQLGetFunctions(m_pDatabase->m_hdbc,
  2121. SQL_API_SQLEXTENDEDFETCH, &wScrollable));
  2122. if (!Check(nRetCode))
  2123. {
  2124. TRACE0("Error: ODBC failure determining whether recordset is scrollable.\n");
  2125. ThrowDBException(nRetCode);
  2126. }
  2127. m_bScrollable = wScrollable;
  2128. if (!m_bScrollable)
  2129. {
  2130. #ifdef _DEBUG
  2131. if (afxTraceFlags & traceDatabase)
  2132. {
  2133. TRACE0("Warning: SQLExtendedFetch not supported by driver\n");
  2134. TRACE0("and/or cursor library not loaded. Opening forwardOnly.\n");
  2135. TRACE0("for use with SQLFetch.\n");
  2136. }
  2137. #endif
  2138. m_bUpdatable = FALSE;
  2139. return;
  2140. }
  2141. char szResult[30];
  2142. SWORD nResult;
  2143. // require ODBC v2.0
  2144. AFX_SQL_SYNC(::SQLGetInfo(m_pDatabase->m_hdbc, SQL_ODBC_VER,
  2145. &szResult, _countof(szResult), &nResult));
  2146. if (!Check(nRetCode))
  2147. {
  2148. TRACE0("Error: ODBC failure checking for driver capabilities.\n");
  2149. ThrowDBException(nRetCode);
  2150. }
  2151. if (szResult[0] == '0' && szResult[1] < '2')
  2152. ThrowDBException(AFX_SQL_ERROR_ODBC_V2_REQUIRED);
  2153. }
  2154. // Check that driver supports requested cursor type
  2155. DWORD CRecordset::VerifyCursorSupport()
  2156. {
  2157. RETCODE nRetCode;
  2158. SWORD nResult;
  2159. UDWORD dwDriverScrollOptions;
  2160. AFX_SQL_SYNC(::SQLGetInfo(m_pDatabase->m_hdbc, SQL_SCROLL_OPTIONS,
  2161. &dwDriverScrollOptions, sizeof(dwDriverScrollOptions), &nResult));
  2162. if (!Check(nRetCode))
  2163. {
  2164. TRACE0("Error: ODBC failure checking for driver capabilities.\n");
  2165. ThrowDBException(nRetCode);
  2166. }
  2167. SDWORD dwScrollOptions = SQL_CURSOR_FORWARD_ONLY;
  2168. if (m_nOpenType == dynaset)
  2169. {
  2170. // Dynaset support requires ODBC's keyset driven cursor model
  2171. if (!(dwDriverScrollOptions & SQL_SO_KEYSET_DRIVEN))
  2172. ThrowDBException(AFX_SQL_ERROR_DYNASET_NOT_SUPPORTED);
  2173. dwScrollOptions = SQL_CURSOR_KEYSET_DRIVEN;
  2174. }
  2175. else if (m_nOpenType == snapshot)
  2176. {
  2177. // Snapshot support requires ODBC's static cursor model
  2178. if (!(dwDriverScrollOptions & SQL_SO_STATIC))
  2179. ThrowDBException(AFX_SQL_ERROR_SNAPSHOT_NOT_SUPPORTED);
  2180. dwScrollOptions = SQL_CURSOR_STATIC;
  2181. }
  2182. else
  2183. {
  2184. // Dynamic cursor support requires ODBC's dynamic cursor model
  2185. if (!(dwDriverScrollOptions & SQL_SO_DYNAMIC))
  2186. ThrowDBException(AFX_SQL_ERROR_DYNAMIC_CURSOR_NOT_SUPPORTED);
  2187. dwScrollOptions = SQL_CURSOR_DYNAMIC;
  2188. }
  2189. return dwScrollOptions;
  2190. }
  2191. void CRecordset::AllocAndCacheFieldInfo()
  2192. {
  2193. ASSERT(GetODBCFieldCount() < 0);
  2194. ASSERT(m_rgODBCFieldInfos == NULL);
  2195. RETCODE nRetCode;
  2196. SWORD nActualLen;
  2197. // Cache the number of result columns
  2198. AFX_ODBC_CALL(::SQLNumResultCols(m_hstmt, &m_nResultCols));
  2199. if (!Check(nRetCode))
  2200. {
  2201. TRACE0("Error: Can't get field info.\n");
  2202. ThrowDBException(nRetCode);
  2203. }
  2204. // If there are no fields quit now
  2205. if (m_nResultCols == 0)
  2206. return;
  2207. // Allocate buffer and get the ODBC meta data
  2208. m_rgODBCFieldInfos = new CODBCFieldInfo[m_nResultCols];
  2209. LPSTR lpszFieldName;
  2210. #ifdef _UNICODE
  2211. // Need proxy to temporarily store non-UNICODE name
  2212. lpszFieldName = new char[MAX_FNAME_LEN + 1];
  2213. #endif
  2214. // Get the field info each field
  2215. for (WORD n = 1; n <= GetODBCFieldCount(); n++)
  2216. {
  2217. #ifndef _UNICODE
  2218. // Reset the buffer to point to next element
  2219. lpszFieldName =
  2220. m_rgODBCFieldInfos[n - 1].m_strName.GetBuffer(MAX_FNAME_LEN + 1);
  2221. #endif
  2222. AFX_ODBC_CALL(::SQLDescribeCol(m_hstmt, n,
  2223. (UCHAR*)lpszFieldName, MAX_FNAME_LEN, &nActualLen,
  2224. &m_rgODBCFieldInfos[n - 1].m_nSQLType,
  2225. &m_rgODBCFieldInfos[n - 1].m_nPrecision,
  2226. &m_rgODBCFieldInfos[n - 1].m_nScale,
  2227. &m_rgODBCFieldInfos[n - 1].m_nNullability));
  2228. #ifndef _UNICODE
  2229. m_rgODBCFieldInfos[n - 1].m_strName.ReleaseBuffer(nActualLen);
  2230. #else
  2231. // Copy the proxy data to correct element
  2232. m_rgODBCFieldInfos[n - 1].m_strName = lpszFieldName;
  2233. #endif
  2234. if (!Check(nRetCode))
  2235. {
  2236. TRACE1("Error: ODBC failure getting field #%d info.\n", n);
  2237. ThrowDBException(nRetCode);
  2238. }
  2239. }
  2240. #ifdef _UNICODE
  2241. delete[] lpszFieldName;
  2242. #endif
  2243. }
  2244. void CRecordset::AllocRowset()
  2245. {
  2246. if (m_dwOptions & useMultiRowFetch)
  2247. SetRowsetSize(m_dwRowsetSize);
  2248. else
  2249. {
  2250. // Not using bulk row fetch, set rowset size to 1
  2251. m_rgRowStatus = new WORD[1];
  2252. m_dwRowsetSize = 1;
  2253. }
  2254. }
  2255. void CRecordset::FreeRowset()
  2256. {
  2257. // Delete the rowset status
  2258. delete [] m_rgRowStatus;
  2259. m_rgRowStatus = NULL;
  2260. if (m_dwOptions & useMultiRowFetch &&
  2261. !(m_dwOptions & userAllocMultiRowBuffers))
  2262. {
  2263. // Calling virtual function, DoBulkFieldExchange, here is bad
  2264. // because Close then FreeRowset may get called from destructor.
  2265. // There is no simple choice however if RFX_Bulk functions do
  2266. // a memory allocation. The net result is that users MUST call
  2267. // Close explicitly (rather than relying on destructor) if
  2268. // using multi row fetches, otherwise they will get a memory leak.
  2269. // If rowset already allocated, delete old rowset buffers
  2270. if (m_dwAllocatedRowsetSize != 0)
  2271. {
  2272. CFieldExchange fx(CFieldExchange::DeleteMultiRowBuffer, this);
  2273. DoBulkFieldExchange(&fx);
  2274. }
  2275. }
  2276. m_dwAllocatedRowsetSize = 0;
  2277. }
  2278. void CRecordset::EnableBookmarks()
  2279. {
  2280. // Turn on bookmark support if necessary
  2281. if (m_dwOptions & useBookmarks)
  2282. {
  2283. RETCODE nRetCode;
  2284. // Set stmt option if bookmarks supported by driver
  2285. if (m_pDatabase->GetBookmarkPersistence() & SQL_BP_SCROLL)
  2286. {
  2287. AFX_SQL_SYNC(::SQLSetStmtOption(m_hstmt, SQL_USE_BOOKMARKS,
  2288. SQL_UB_ON));
  2289. if (!Check(nRetCode))
  2290. {
  2291. TRACE0("Error: Can't enable bookmark support.\n");
  2292. ThrowDBException(nRetCode);
  2293. }
  2294. }
  2295. }
  2296. }
  2297. // Determine whether to use SQLSetPos or positioned update SQL
  2298. void CRecordset::SetUpdateMethod()
  2299. {
  2300. // Determine update method
  2301. if (m_pDatabase->m_dwUpdateOptions & AFX_SQL_SETPOSUPDATES)
  2302. m_bUseUpdateSQL = FALSE;
  2303. else if (m_pDatabase->m_dwUpdateOptions & AFX_SQL_POSITIONEDSQL)
  2304. m_bUseUpdateSQL = TRUE;
  2305. else
  2306. m_bUpdatable = FALSE;
  2307. }
  2308. // Determine which type of concurrency to set, set it and cursor type
  2309. void CRecordset::SetConcurrencyAndCursorType(HSTMT hstmt, DWORD dwScrollOptions)
  2310. {
  2311. RETCODE nRetCode;
  2312. SWORD nResult;
  2313. m_dwConcurrency = SQL_CONCUR_READ_ONLY;
  2314. if ((m_bUpdatable || m_bAppendable) && m_pDatabase->m_bUpdatable)
  2315. {
  2316. AFX_SQL_SYNC(::SQLGetInfo(m_pDatabase->m_hdbc, SQL_SCROLL_CONCURRENCY,
  2317. &m_dwDriverConcurrency, sizeof(m_dwDriverConcurrency), &nResult));
  2318. if (!Check(nRetCode))
  2319. {
  2320. TRACE0("Error: ODBC failure checking recordset updatability.\n");
  2321. ThrowDBException(nRetCode);
  2322. }
  2323. if (m_nLockMode == pessimistic)
  2324. {
  2325. if (m_dwDriverConcurrency & SQL_SCCO_LOCK)
  2326. m_dwConcurrency = SQL_CONCUR_LOCK;
  2327. #ifdef _DEBUG
  2328. else
  2329. if (afxTraceFlags & traceDatabase)
  2330. TRACE0("Warning: locking not supported, setting recordset read only.\n");
  2331. #endif
  2332. }
  2333. else
  2334. {
  2335. // Use cheapest, most concurrent model
  2336. if (m_dwDriverConcurrency & SQL_SCCO_OPT_ROWVER)
  2337. m_dwConcurrency = SQL_CONCUR_ROWVER;
  2338. else if (m_dwDriverConcurrency & SQL_SCCO_OPT_VALUES)
  2339. m_dwConcurrency = SQL_CONCUR_VALUES;
  2340. else if (m_dwDriverConcurrency & SQL_SCCO_LOCK)
  2341. m_dwConcurrency = SQL_CONCUR_LOCK;
  2342. }
  2343. }
  2344. // Set cursor type (Let rowset size default to 1).
  2345. AFX_SQL_SYNC(::SQLSetStmtOption(hstmt, SQL_CURSOR_TYPE, dwScrollOptions));
  2346. if (!Check(nRetCode))
  2347. {
  2348. TRACE0("Error: ODBC failure setting recordset cursor type.\n");
  2349. ThrowDBException(nRetCode);
  2350. }
  2351. // Set the concurrency model (NOTE: may have to reset concurrency later).
  2352. AFX_SQL_SYNC(::SQLSetStmtOption(hstmt, SQL_CONCURRENCY, m_dwConcurrency));
  2353. if (!Check(nRetCode))
  2354. {
  2355. TRACE0("Error: ODBC failure setting recordset concurrency.\n");
  2356. ThrowDBException(nRetCode);
  2357. }
  2358. }
  2359. // Is there a join, stored proc call, GROUP BY, UNION or missing FROM?
  2360. BOOL CRecordset::IsSQLUpdatable(LPCTSTR lpszSQL)
  2361. {
  2362. // Parse for query procedure call keyword or return param
  2363. if (!(_tcsnicmp(lpszSQL, _afxCall, lstrlen(_afxCall)-1) == 0 ||
  2364. _tcsnicmp(lpszSQL, _afxParamCall, lstrlen(_afxParamCall)-1) == 0))
  2365. // Assume this is a select query
  2366. return IsSelectQueryUpdatable(lpszSQL);
  2367. else
  2368. // Don't know the table name to update in procedure call
  2369. return FALSE;
  2370. }
  2371. BOOL CRecordset::IsSelectQueryUpdatable(LPCTSTR lpszSQL)
  2372. {
  2373. LPCTSTR lpchTokenFrom;
  2374. LPCTSTR lpchToken;
  2375. LPCTSTR lpchTokenNext;
  2376. LPTSTR lpszSQLStart;
  2377. CString strSQL = lpszSQL;
  2378. lpchTokenFrom = FindSQLToken(strSQL, _afxFrom);
  2379. if (lpchTokenFrom == NULL)
  2380. {
  2381. #ifdef _DEBUG
  2382. if (afxTraceFlags & traceDatabase)
  2383. TRACE0("Warning: Missing ' FROM ', recordset not updatable \n");
  2384. #endif
  2385. return FALSE;
  2386. }
  2387. lpchToken = FindSQLToken(strSQL, _T(" GROUP BY "));
  2388. if (lpchToken != NULL)
  2389. {
  2390. #ifdef _DEBUG
  2391. if (afxTraceFlags & traceDatabase)
  2392. TRACE0("Warning: SQL contains ' GROUP BY ', recordset not updatable \n");
  2393. #endif
  2394. return FALSE;
  2395. }
  2396. lpchToken = FindSQLToken(strSQL, _T(" UNION "));
  2397. if (lpchToken != NULL)
  2398. {
  2399. #ifdef _DEBUG
  2400. if (afxTraceFlags & traceDatabase)
  2401. TRACE0("Warning: SQL contains ' UNION ', recordset not updatable \n");
  2402. #endif
  2403. return FALSE;
  2404. }
  2405. // Find next token after FROM (can't have HAVING clause without GROUP BY)
  2406. lpchToken = FindSQLToken(strSQL, _afxWhere);
  2407. lpchTokenNext = FindSQLToken(strSQL, _afxOrderBy);
  2408. lpszSQLStart = strSQL.GetBuffer(0);
  2409. if (lpchTokenNext == NULL)
  2410. lpchTokenNext = lpchToken;
  2411. else if (lpchToken != NULL && lpchToken < lpchTokenNext)
  2412. lpchTokenNext = lpchToken;
  2413. if (lpchTokenNext != NULL)
  2414. {
  2415. int nFromLength = lpchTokenNext - lpchTokenFrom;
  2416. memcpy(lpszSQLStart, lpchTokenFrom, nFromLength*sizeof(TCHAR));
  2417. lpszSQLStart[nFromLength] = '\0';
  2418. }
  2419. else
  2420. lstrcpy(lpszSQLStart, lpchTokenFrom);
  2421. strSQL.ReleaseBuffer();
  2422. if (IsJoin(strSQL))
  2423. {
  2424. #ifdef _DEBUG
  2425. if (afxTraceFlags & traceDatabase)
  2426. TRACE0("Warning: SQL contains join, recordset not updatable \n");
  2427. #endif
  2428. return FALSE;
  2429. }
  2430. // Cache table name (skip over " FROM ")
  2431. m_strTableName = strSQL.Right(strSQL.GetLength()-6);
  2432. return TRUE;
  2433. }
  2434. // Check FROM clause for join syntax
  2435. BOOL PASCAL CRecordset::IsJoin(LPCTSTR lpszJoinClause)
  2436. {
  2437. // Look for comma in join clause
  2438. if (FindSQLToken(lpszJoinClause, _afxComma) != NULL)
  2439. return TRUE;
  2440. // Look for outer join clause
  2441. if (FindSQLToken(lpszJoinClause, _T(" JOIN ")) != NULL)
  2442. return TRUE;
  2443. return FALSE;
  2444. }
  2445. // Searches string for given token not in single quotes or brackets
  2446. LPCTSTR PASCAL CRecordset::FindSQLToken(LPCTSTR lpszSQL, LPCTSTR lpszSQLToken)
  2447. {
  2448. BOOL bInLiteral;
  2449. BOOL bInBrackets;
  2450. int nLeftBrackets;
  2451. int nRightBrackets;
  2452. LPCTSTR lpch;
  2453. LPCTSTR lpchSQLStart;
  2454. LPCTSTR lpszFoundToken;
  2455. int nTokenOffset = 0;
  2456. CString strSQL = lpszSQL;
  2457. strSQL.MakeUpper();
  2458. lpszFoundToken = strSQL.GetBuffer(0);
  2459. lpchSQLStart = lpszFoundToken;
  2460. do
  2461. {
  2462. lpszFoundToken = _tcsstr(lpszFoundToken + nTokenOffset, lpszSQLToken);
  2463. if (lpszFoundToken == NULL)
  2464. {
  2465. strSQL.ReleaseBuffer();
  2466. return NULL;
  2467. }
  2468. bInLiteral = bInBrackets = FALSE;
  2469. nLeftBrackets = nRightBrackets = 0;
  2470. // Check if embedded in literal or brackets
  2471. for (lpch = lpchSQLStart; lpch < lpszFoundToken; lpch = _tcsinc(lpch))
  2472. {
  2473. if (*lpch == _afxLiteralSeparator)
  2474. {
  2475. // Skip if escape literal
  2476. if (*_tcsinc(lpch) == _afxLiteralSeparator)
  2477. lpch = _tcsinc(lpch);
  2478. else
  2479. bInLiteral = !bInLiteral;
  2480. }
  2481. else if (!bInLiteral && (*lpch == '['))
  2482. {
  2483. // Skip if escape left bracket
  2484. if (*_tcsinc(lpch) == '[')
  2485. lpch = _tcsinc(lpch);
  2486. else
  2487. {
  2488. nLeftBrackets++;
  2489. if ((nLeftBrackets - nRightBrackets) > 0)
  2490. bInBrackets = TRUE;
  2491. else
  2492. bInBrackets = FALSE;
  2493. }
  2494. }
  2495. else if (!bInLiteral && (*lpch == ']'))
  2496. {
  2497. // Skip if escape right bracket
  2498. if (*_tcsinc(lpch) == ']')
  2499. lpch = _tcsinc(lpch);
  2500. else
  2501. {
  2502. nRightBrackets++;
  2503. if ((nLeftBrackets - nRightBrackets) > 0)
  2504. bInBrackets = TRUE;
  2505. else
  2506. bInBrackets = FALSE;
  2507. }
  2508. }
  2509. }
  2510. // If first iteration, reset the offset to jump over found token
  2511. if (nTokenOffset == 0)
  2512. nTokenOffset = lstrlen(lpszSQLToken);
  2513. } while (bInLiteral || bInBrackets);
  2514. lpszFoundToken = lpszSQL + (lpszFoundToken - lpchSQLStart);
  2515. strSQL.ReleaseBuffer();
  2516. return lpszFoundToken;
  2517. }
  2518. // Bind fields (if not already bound), then retrieve 1st record
  2519. void CRecordset::InitRecord()
  2520. {
  2521. // fields to bind
  2522. if (m_nFields != 0)
  2523. {
  2524. m_nFieldsBound = BindFieldsToColumns();
  2525. // m_nFields doesn't reflect number of
  2526. // RFX_ output column calls in Do[Bulk]FieldExchange
  2527. ASSERT((int)m_nFields == m_nFieldsBound);
  2528. // Allocate the data cache if necessary
  2529. if (m_nFields > 0 && m_bCheckCacheForDirtyFields)
  2530. AllocDataCache();
  2531. }
  2532. else
  2533. // No fields to bind, don't attempt to bind again
  2534. m_nFieldsBound = -1;
  2535. }
  2536. void CRecordset::ResetCursor()
  2537. {
  2538. m_bEOFSeen = m_bBOF = m_bEOF = FALSE;
  2539. m_bDeleted = FALSE;
  2540. m_lCurrentRecord = AFX_CURRENT_RECORD_BOF;
  2541. m_lRecordCount = 0;
  2542. }
  2543. void CRecordset::CheckRowsetCurrencyStatus(UWORD wFetchType, long nRows)
  2544. {
  2545. if (!m_bScrollable && wFetchType != SQL_FETCH_NEXT)
  2546. {
  2547. TRACE0("Error: forward-only recordsets only support MoveNext.\n");
  2548. ThrowDBException(AFX_SQL_ERROR_RECORDSET_FORWARD_ONLY);
  2549. }
  2550. if (IsEOF() && IsBOF())
  2551. {
  2552. // Can't position cursor if recordset empty
  2553. TRACE0("Error: attempted to position cursor on empty recordset.\n");
  2554. ThrowDBException(AFX_SQL_ERROR_NO_DATA_FOUND);
  2555. }
  2556. if (m_nOpenType != dynamic)
  2557. {
  2558. if (IsEOF() && (wFetchType == SQL_FETCH_NEXT ||
  2559. (wFetchType == SQL_FETCH_RELATIVE && nRows > 0)))
  2560. {
  2561. // if already at EOF, throw an exception
  2562. TRACE0("Error: attempted to move past EOF.\n");
  2563. ThrowDBException(AFX_SQL_ERROR_NO_DATA_FOUND);
  2564. }
  2565. else if (IsBOF() && (wFetchType == SQL_FETCH_PRIOR ||
  2566. (wFetchType == SQL_FETCH_RELATIVE && nRows < 0)))
  2567. {
  2568. // if already at BOF, throw an exception
  2569. TRACE0("Error: attempted to move before BOF.\n");
  2570. ThrowDBException(AFX_SQL_ERROR_NO_DATA_FOUND);
  2571. }
  2572. }
  2573. }
  2574. RETCODE CRecordset::FetchData(UWORD wFetchType, SDWORD nRow,
  2575. DWORD* pdwRowsFetched)
  2576. {
  2577. RETCODE nRetCode;
  2578. if (m_nOpenType == forwardOnly && !(m_dwOptions & useExtendedFetch))
  2579. {
  2580. ASSERT(wFetchType == SQL_FETCH_NEXT);
  2581. AFX_ODBC_CALL(::SQLFetch(m_hstmt));
  2582. *pdwRowsFetched = 1;
  2583. m_bDeleted = FALSE;
  2584. }
  2585. else
  2586. {
  2587. AFX_ODBC_CALL(::SQLExtendedFetch(m_hstmt, wFetchType,
  2588. nRow, pdwRowsFetched, m_rgRowStatus));
  2589. // Set deleted status
  2590. m_bDeleted = GetRowStatus(1) == SQL_ROW_DELETED;
  2591. }
  2592. CheckRowsetError(nRetCode);
  2593. return nRetCode;
  2594. }
  2595. void CRecordset::SkipDeletedRecords(UWORD wFetchType, long nRows,
  2596. DWORD* pdwRowsFetched, RETCODE* pnRetCode)
  2597. {
  2598. ASSERT(!(m_dwOptions & useMultiRowFetch));
  2599. ASSERT(wFetchType == SQL_FETCH_RELATIVE ||
  2600. wFetchType == SQL_FETCH_FIRST ||
  2601. wFetchType == SQL_FETCH_NEXT ||
  2602. wFetchType == SQL_FETCH_LAST ||
  2603. wFetchType == SQL_FETCH_PRIOR);
  2604. ASSERT(nRows != 0);
  2605. UWORD wDeletedFetchType = wFetchType;
  2606. DWORD dwDeletedRows = abs(nRows);
  2607. BOOL m_bDone;
  2608. switch (wFetchType)
  2609. {
  2610. case SQL_FETCH_FIRST:
  2611. wDeletedFetchType = SQL_FETCH_NEXT;
  2612. break;
  2613. case SQL_FETCH_LAST:
  2614. wDeletedFetchType = SQL_FETCH_PRIOR;
  2615. break;
  2616. case SQL_FETCH_RELATIVE:
  2617. if (nRows > 0)
  2618. wDeletedFetchType = SQL_FETCH_NEXT;
  2619. else
  2620. wDeletedFetchType = SQL_FETCH_PRIOR;
  2621. break;
  2622. }
  2623. // First fetch is as expected unless relative fetch
  2624. if (wFetchType != SQL_FETCH_RELATIVE)
  2625. {
  2626. *pnRetCode = FetchData(wFetchType, 1, pdwRowsFetched);
  2627. m_bDone = !m_bDeleted;
  2628. }
  2629. else
  2630. {
  2631. // Since deleted records must be skipped Move(n)
  2632. // must be turned into n MoveNext/MovePrev calls
  2633. *pnRetCode = FetchData(wDeletedFetchType, 1, pdwRowsFetched);
  2634. if (!m_bDeleted)
  2635. {
  2636. dwDeletedRows--;
  2637. m_bDone = dwDeletedRows == 0;
  2638. }
  2639. else
  2640. m_bDone = FALSE;
  2641. }
  2642. // Continue fetching until all req'd deleted records skipped
  2643. while (*pnRetCode != SQL_NO_DATA_FOUND && !m_bDone)
  2644. {
  2645. *pnRetCode = FetchData(wDeletedFetchType, 1, pdwRowsFetched);
  2646. if (wFetchType == SQL_FETCH_RELATIVE)
  2647. {
  2648. if (!m_bDeleted)
  2649. {
  2650. dwDeletedRows--;
  2651. m_bDone = dwDeletedRows == 0;
  2652. }
  2653. else
  2654. m_bDone = FALSE;
  2655. }
  2656. else
  2657. m_bDone = !m_bDeleted;
  2658. }
  2659. }
  2660. void CRecordset::SetRowsetCurrencyStatus(RETCODE nRetCode,
  2661. UWORD wFetchType, long nRows, DWORD dwRowsFetched)
  2662. {
  2663. UNUSED_ALWAYS(dwRowsFetched);
  2664. // Set the fetch direction
  2665. int nDirection = 0;
  2666. switch (wFetchType)
  2667. {
  2668. case SQL_FETCH_FIRST:
  2669. nDirection = 1;
  2670. if (nRetCode == SQL_NO_DATA_FOUND)
  2671. {
  2672. m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED;
  2673. m_lRecordCount = 0;
  2674. }
  2675. else
  2676. m_lCurrentRecord = 0;
  2677. break;
  2678. case SQL_FETCH_NEXT:
  2679. nDirection = 1;
  2680. AfxSetCurrentRecord(&m_lCurrentRecord, nRows, nRetCode);
  2681. AfxSetRecordCount(&m_lRecordCount, m_lCurrentRecord,
  2682. m_bEOFSeen, nRetCode);
  2683. // This is the only way to know you've hit the end (m_bEOFSeen)
  2684. if (!m_bEOFSeen && nRetCode == SQL_NO_DATA_FOUND &&
  2685. m_lRecordCount == m_lCurrentRecord + 1)
  2686. {
  2687. m_bEOFSeen = TRUE;
  2688. }
  2689. break;
  2690. case SQL_FETCH_LAST:
  2691. nDirection = -1;
  2692. if (nRetCode == SQL_NO_DATA_FOUND)
  2693. {
  2694. m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED;
  2695. m_lRecordCount = 0;
  2696. }
  2697. else if (m_bEOFSeen)
  2698. m_lCurrentRecord = m_lRecordCount - 1;
  2699. else
  2700. m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED;
  2701. break;
  2702. case SQL_FETCH_PRIOR:
  2703. nDirection = -1;
  2704. // If doing MovePrev after m_bEOF, don't increment current rec
  2705. if (!m_bEOF)
  2706. AfxSetCurrentRecord(&m_lCurrentRecord, nRows, nRetCode);
  2707. break;
  2708. case SQL_FETCH_RELATIVE:
  2709. nDirection = nRows;
  2710. AfxSetCurrentRecord(&m_lCurrentRecord, nRows, nRetCode);
  2711. AfxSetRecordCount(&m_lRecordCount, m_lCurrentRecord,
  2712. m_bEOFSeen, nRetCode);
  2713. break;
  2714. case SQL_FETCH_ABSOLUTE:
  2715. nDirection = nRows;
  2716. if (nRetCode != SQL_NO_DATA_FOUND)
  2717. {
  2718. if (nRows > 0)
  2719. m_lCurrentRecord = nRows - 1;
  2720. else if (m_bEOFSeen)
  2721. m_lCurrentRecord = m_lRecordCount + nRows;
  2722. else
  2723. m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED;
  2724. }
  2725. else
  2726. m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED;
  2727. AfxSetRecordCount(&m_lRecordCount, m_lCurrentRecord,
  2728. m_bEOFSeen, nRetCode);
  2729. break;
  2730. case SQL_FETCH_BOOKMARK:
  2731. nDirection = 0;
  2732. m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED;
  2733. break;
  2734. }
  2735. // Set the BOF/EOF flags
  2736. if (nRetCode == SQL_NO_DATA_FOUND)
  2737. {
  2738. if (wFetchType == SQL_FETCH_FIRST || wFetchType == SQL_FETCH_LAST ||
  2739. wFetchType == SQL_FETCH_BOOKMARK)
  2740. {
  2741. // If MoveFirst/MoveLast fails, result set is empty
  2742. // If SetBookmark fails, currency undefined
  2743. m_bEOF = m_bBOF = TRUE;
  2744. }
  2745. else
  2746. {
  2747. m_bEOF = nDirection >= 0;
  2748. m_bBOF = !m_bEOF;
  2749. }
  2750. }
  2751. else
  2752. {
  2753. m_bEOF = m_bBOF = FALSE;
  2754. }
  2755. }
  2756. void CRecordset::RefreshRowset(WORD wRow, WORD wLockType)
  2757. {
  2758. ASSERT(IsOpen());
  2759. ASSERT(m_dwOptions & useMultiRowFetch);
  2760. RETCODE nRetCode;
  2761. AFX_ODBC_CALL(::SQLSetPos(m_hstmt, wRow, SQL_REFRESH, wLockType));
  2762. // Need to fixup bound fields in some cases
  2763. if (m_nFields > 0 && !IsEOF() && !IsBOF() &&
  2764. !(m_dwOptions & useMultiRowFetch))
  2765. {
  2766. Fixups();
  2767. }
  2768. }
  2769. void CRecordset::SetRowsetCursorPosition(WORD wRow, WORD wLockType)
  2770. {
  2771. ASSERT(IsOpen());
  2772. ASSERT(m_dwOptions & useMultiRowFetch);
  2773. RETCODE nRetCode;
  2774. AFX_ODBC_CALL(::SQLSetPos(m_hstmt, wRow, SQL_POSITION, wLockType));
  2775. }
  2776. // "SELECT <user column name list> FROM <table name>"
  2777. void CRecordset::BuildSelectSQL()
  2778. {
  2779. ASSERT_VALID(this);
  2780. ASSERT(m_hstmt != SQL_NULL_HSTMT);
  2781. // Ignore queries with procedure call keyword or output param
  2782. if (!(_tcsnicmp(m_strSQL, _afxCall, lstrlen(_afxCall)-1) == 0 ||
  2783. _tcsnicmp(m_strSQL, _afxParamCall, lstrlen(_afxParamCall)-1) == 0))
  2784. {
  2785. // Ignore queries already built
  2786. if (_tcsnicmp(m_strSQL, _afxSelect, lstrlen(_afxSelect)-1) != 0)
  2787. {
  2788. // Assume m_strSQL specifies table name
  2789. ASSERT(m_nFields != 0);
  2790. CString strTableName;
  2791. strTableName = m_strSQL;
  2792. m_strSQL.Empty();
  2793. m_strSQL = _afxSelect;
  2794. // Set all fields dirty. AppendNames only outputs dirty field names
  2795. SetFieldDirty(NULL);
  2796. if (AppendNames(&m_strSQL, _T(",")) == 0)
  2797. {
  2798. TRACE0("Error: no field names - at least 1 required.\n");
  2799. ThrowDBException(AFX_SQL_ERROR_EMPTY_COLUMN_LIST);
  2800. }
  2801. // Overwrite final ',' separator with ' '
  2802. ASSERT(m_strSQL[m_strSQL.GetLength()-1] == ',');
  2803. m_strSQL.SetAt(m_strSQL.GetLength()-1, ' ');
  2804. m_strSQL += _afxFrom;
  2805. m_strSQL += strTableName;
  2806. }
  2807. }
  2808. }
  2809. // Add the filter and sort strings to query SQL
  2810. void CRecordset::AppendFilterAndSortSQL()
  2811. {
  2812. if (!m_strFilter.IsEmpty())
  2813. {
  2814. m_strSQL += _afxWhere;
  2815. m_strSQL += m_strFilter;
  2816. }
  2817. if (!m_strSort.IsEmpty())
  2818. {
  2819. m_strSQL += _afxOrderBy;
  2820. m_strSQL += m_strSort;
  2821. }
  2822. }
  2823. // Check for required SQLGetData support and do limited SQL parsing
  2824. BOOL CRecordset::IsRecordsetUpdatable()
  2825. {
  2826. // Do limited SQL parsing to determine if SQL updatable
  2827. if (!IsSQLUpdatable(m_strSQL))
  2828. return FALSE;
  2829. // Updatable recordsets with long binary columns must support
  2830. // SQL_GD_BOUND to use SQLSetPos, otherwise must use SQL updates
  2831. BOOL bUpdatable = TRUE;
  2832. if (m_bLongBinaryColumns && !m_bUseUpdateSQL)
  2833. {
  2834. // Set non-updatable if you can't use SQLGetData on bound columns
  2835. if (!(m_pDatabase->m_dwUpdateOptions & AFX_SQL_GDBOUND))
  2836. {
  2837. // Okay can't use SetPos, try and use positioned update SQL
  2838. if (m_pDatabase->m_dwUpdateOptions & AFX_SQL_POSITIONEDSQL)
  2839. {
  2840. m_bUseUpdateSQL = TRUE;
  2841. #ifdef _DEBUG
  2842. if (afxTraceFlags & traceDatabase)
  2843. {
  2844. TRACE0("Warning: Can't use SQLSetPos due to lack of SQLGetData support.\n");
  2845. TRACE0("\tWill use positioned update SQL.\n");
  2846. }
  2847. #endif
  2848. }
  2849. else
  2850. {
  2851. #ifdef _DEBUG
  2852. if (afxTraceFlags & traceDatabase)
  2853. TRACE0("Warning: Setting recordset read only due to lack of SQLGetData support.\n");
  2854. #endif
  2855. bUpdatable = FALSE;
  2856. }
  2857. }
  2858. }
  2859. return bUpdatable;
  2860. }
  2861. // Execute the update (or delete) using SQLSetPos
  2862. void CRecordset::ExecuteSetPosUpdate()
  2863. {
  2864. UWORD wExpectedRowStatus;
  2865. UWORD wPosOption;
  2866. if (m_nEditMode == noMode)
  2867. {
  2868. wPosOption = SQL_DELETE;
  2869. wExpectedRowStatus = SQL_ROW_DELETED;
  2870. }
  2871. else
  2872. {
  2873. if (m_nEditMode == edit)
  2874. {
  2875. wPosOption = SQL_UPDATE;
  2876. wExpectedRowStatus = SQL_ROW_UPDATED;
  2877. }
  2878. else
  2879. {
  2880. wPosOption = SQL_ADD;
  2881. wExpectedRowStatus = SQL_ROW_ADDED;
  2882. }
  2883. }
  2884. BindFieldsForUpdate();
  2885. RETCODE nRetCode;
  2886. AFX_ODBC_CALL(::SQLSetPos(m_hstmt, 1, wPosOption, SQL_LOCK_NO_CHANGE));
  2887. if (!Check(nRetCode))
  2888. {
  2889. TRACE0("Error: failure updating record.\n");
  2890. AfxThrowDBException(nRetCode, m_pDatabase, m_hstmt);
  2891. }
  2892. // Only have data-at-execution columns for CLongBinary columns
  2893. if (nRetCode == SQL_NEED_DATA)
  2894. SendLongBinaryData(m_hstmt);
  2895. // This should only fail if SQLSetPos returned SQL_SUCCESS_WITH_INFO explaining why
  2896. if (nRetCode == SQL_SUCCESS_WITH_INFO && GetRowStatus(1) != wExpectedRowStatus)
  2897. ThrowDBException(AFX_SQL_ERROR_UPDATE_DELETE_FAILED);
  2898. UnbindFieldsForUpdate();
  2899. }
  2900. // Prepare for sending update SQL by initializing m_hstmtUpdate
  2901. void CRecordset::PrepareUpdateHstmt()
  2902. {
  2903. RETCODE nRetCode;
  2904. if (m_hstmtUpdate == SQL_NULL_HSTMT)
  2905. {
  2906. AFX_SQL_SYNC(::SQLAllocStmt(m_pDatabase->m_hdbc, &m_hstmtUpdate));
  2907. if (!Check(nRetCode))
  2908. {
  2909. TRACE0("Error: failure to allocate update statement.\n");
  2910. AfxThrowDBException(nRetCode, m_pDatabase, m_hstmtUpdate);
  2911. }
  2912. }
  2913. else
  2914. {
  2915. AFX_SQL_SYNC(::SQLFreeStmt(m_hstmtUpdate, SQL_CLOSE));
  2916. if (!Check(nRetCode))
  2917. goto LErrRetCode;
  2918. // Re-use (prepared) hstmt & param binding if optimizeBulkAdd option
  2919. if(!(m_dwOptions & optimizeBulkAdd))
  2920. {
  2921. AFX_SQL_SYNC(::SQLFreeStmt(m_hstmtUpdate, SQL_RESET_PARAMS));
  2922. if (!Check(nRetCode))
  2923. {
  2924. LErrRetCode:
  2925. // Bad hstmt, free it and allocate another one
  2926. AFX_SQL_SYNC(::SQLFreeStmt(m_hstmtUpdate, SQL_DROP));
  2927. m_hstmtUpdate = SQL_NULL_HSTMT;
  2928. AFX_SQL_SYNC(::SQLAllocStmt(m_pDatabase->m_hdbc, &m_hstmtUpdate));
  2929. if (!Check(nRetCode))
  2930. {
  2931. TRACE0("Error: failure to allocate update statement.\n");
  2932. AfxThrowDBException(nRetCode, m_pDatabase, m_hstmtUpdate);
  2933. }
  2934. }
  2935. }
  2936. }
  2937. }
  2938. // Build the UPDATE, INSERT or DELETE SQL
  2939. void CRecordset::BuildUpdateSQL()
  2940. {
  2941. switch (m_nEditMode)
  2942. {
  2943. case noMode:
  2944. // DELETE FROM <tablename> WHERE CURRENT OF
  2945. {
  2946. m_strUpdateSQL = _T("DELETE FROM ");
  2947. m_strUpdateSQL += m_strTableName;
  2948. }
  2949. break;
  2950. case addnew:
  2951. // INSERT INTO <tablename> (<colname1>[,<colname2>]) VALUES (?[,?])
  2952. {
  2953. m_strUpdateSQL = _T("INSERT INTO ");
  2954. m_strUpdateSQL += m_strTableName;
  2955. m_strUpdateSQL += _T(" (");
  2956. // Append column names
  2957. AppendNames(&m_strUpdateSQL, _afxComma);
  2958. // overwrite last ',' with ')'
  2959. ASSERT(m_strUpdateSQL[m_strUpdateSQL.GetLength()-1] == ',');
  2960. m_strUpdateSQL.SetAt(m_strUpdateSQL.GetLength()-1, ')');
  2961. // Append values
  2962. m_strUpdateSQL += _T(" VALUES (");
  2963. AppendValues(m_hstmtUpdate, &m_strUpdateSQL, _afxComma);
  2964. // overwrite last ',' with ')'
  2965. ASSERT(m_strUpdateSQL[m_strUpdateSQL.GetLength()-1] == ',');
  2966. m_strUpdateSQL.SetAt(m_strUpdateSQL.GetLength()-1, ')');
  2967. }
  2968. break;
  2969. case edit:
  2970. // UPDATE <tablename> SET <colname1>=?[,<colname2>=?] WHERE CURRENT OF
  2971. {
  2972. m_strUpdateSQL = _T("UPDATE ");
  2973. m_strUpdateSQL += m_strTableName;
  2974. m_strUpdateSQL += _T(" SET ");
  2975. AppendNamesValues(m_hstmtUpdate, &m_strUpdateSQL, _afxComma);
  2976. // overwrite last ',' with ' '
  2977. ASSERT(m_strUpdateSQL[m_strUpdateSQL.GetLength()-1] == ',');
  2978. m_strUpdateSQL.SetAt(m_strUpdateSQL.GetLength()-1, ' ');
  2979. }
  2980. break;
  2981. }
  2982. // Update and Delete need "WHERE CURRENT OF <cursorname>"
  2983. if (m_nEditMode == edit || m_nEditMode == noMode)
  2984. {
  2985. m_strUpdateSQL += _T(" WHERE CURRENT OF ");
  2986. // Cache cursor name assigned by ODBC
  2987. if (m_strCursorName.IsEmpty())
  2988. {
  2989. // Get predefined cursor name from datasource
  2990. RETCODE nRetCode;
  2991. UCHAR szCursorName[MAX_CURSOR_NAME+1];
  2992. SWORD nLength = _countof(szCursorName)-1;
  2993. AFX_SQL_SYNC(::SQLGetCursorName(m_hstmt,
  2994. szCursorName, _countof(szCursorName), &nLength));
  2995. if (!Check(nRetCode))
  2996. ThrowDBException(nRetCode);
  2997. m_strCursorName = (char*)szCursorName;
  2998. }
  2999. m_strUpdateSQL += m_strCursorName;
  3000. }
  3001. m_pDatabase->ReplaceBrackets(m_strUpdateSQL.GetBuffer(0));
  3002. m_strUpdateSQL.ReleaseBuffer();
  3003. // Must prepare the hstmt on first optimized bulk add
  3004. if(m_dwOptions & firstBulkAdd)
  3005. {
  3006. RETCODE nRetCode;
  3007. USES_CONVERSION;
  3008. AFX_ODBC_CALL(::SQLPrepare(m_hstmtUpdate,
  3009. (UCHAR*)T2A((LPTSTR)(LPCTSTR)m_strUpdateSQL), SQL_NTS));
  3010. if (!Check(nRetCode))
  3011. ThrowDBException(nRetCode, m_hstmtUpdate);
  3012. }
  3013. }
  3014. void CRecordset::ExecuteUpdateSQL()
  3015. {
  3016. RETCODE nRetCode;
  3017. if(!(m_dwOptions & optimizeBulkAdd))
  3018. {
  3019. USES_CONVERSION;
  3020. AFX_ODBC_CALL(::SQLExecDirect(m_hstmtUpdate,
  3021. (UCHAR*)T2A((LPTSTR)(LPCTSTR)m_strUpdateSQL), SQL_NTS));
  3022. if (!Check(nRetCode))
  3023. ThrowDBException(nRetCode, m_hstmtUpdate);
  3024. }
  3025. else
  3026. {
  3027. AFX_ODBC_CALL(::SQLExecute(m_hstmtUpdate));
  3028. if (!Check(nRetCode))
  3029. ThrowDBException(nRetCode, m_hstmtUpdate);
  3030. }
  3031. // Only have data-at-execution parameters for CLongBinary columns
  3032. if (nRetCode == SQL_NEED_DATA)
  3033. SendLongBinaryData(m_hstmtUpdate);
  3034. SDWORD lRowsAffected = 0;
  3035. AFX_SQL_SYNC(::SQLRowCount(m_hstmtUpdate, &lRowsAffected));
  3036. if (!Check(nRetCode) || lRowsAffected == -1)
  3037. {
  3038. // Assume 1 row affected if # rows affected can't be determined
  3039. lRowsAffected = 1;
  3040. }
  3041. else
  3042. {
  3043. if (lRowsAffected != 1)
  3044. {
  3045. #ifdef _DEBUG
  3046. if (afxTraceFlags & traceDatabase)
  3047. TRACE1("Warning: %u rows affected by update operation (expected 1).\n",
  3048. lRowsAffected);
  3049. #endif
  3050. ThrowDBException((RETCODE)(lRowsAffected == 0 ?
  3051. AFX_SQL_ERROR_NO_ROWS_AFFECTED :
  3052. AFX_SQL_ERROR_MULTIPLE_ROWS_AFFECTED));
  3053. }
  3054. }
  3055. m_strUpdateSQL.Empty();
  3056. }
  3057. void CRecordset::SendLongBinaryData(HSTMT hstmt)
  3058. {
  3059. RETCODE nRetCode;
  3060. void* pv;
  3061. AFX_ODBC_CALL(::SQLParamData(hstmt, &pv));
  3062. if (!Check(nRetCode))
  3063. {
  3064. // cache away error
  3065. CDBException* pException = new CDBException(nRetCode);
  3066. pException->BuildErrorString(m_pDatabase, hstmt);
  3067. // then cancel Execute operation
  3068. Cancel();
  3069. THROW(pException);
  3070. }
  3071. while (nRetCode == SQL_NEED_DATA)
  3072. {
  3073. CLongBinary* pLongBinary = (CLongBinary*)pv;
  3074. ASSERT_VALID(pLongBinary);
  3075. const BYTE* lpData = (const BYTE*)::GlobalLock(pLongBinary->m_hData);
  3076. ASSERT(lpData != NULL);
  3077. AFX_ODBC_CALL(::SQLPutData(hstmt, (PTR)lpData,
  3078. pLongBinary->m_dwDataLength));
  3079. ::GlobalUnlock(pLongBinary->m_hData);
  3080. if (!Check(nRetCode))
  3081. {
  3082. // cache away error
  3083. CDBException* pException = new CDBException(nRetCode);
  3084. pException->BuildErrorString(m_pDatabase, hstmt);
  3085. // then cancel Execute operation
  3086. Cancel();
  3087. THROW(pException);
  3088. }
  3089. // Check for another DATA_AT_EXEC
  3090. AFX_ODBC_CALL(::SQLParamData(hstmt, &pv));
  3091. if (!Check(nRetCode))
  3092. {
  3093. TRACE0("Error: failure handling long binary value during update.\n");
  3094. ThrowDBException(nRetCode, hstmt);
  3095. }
  3096. }
  3097. }
  3098. //////////////////////////////////////////////////////////////////////////////
  3099. // CRecordset RFX implementations
  3100. void CRecordset::AllocStatusArrays()
  3101. {
  3102. TRY
  3103. {
  3104. if (m_nFields != 0)
  3105. {
  3106. // Allocate buffers to hold field info
  3107. if (m_rgFieldInfos == NULL)
  3108. {
  3109. m_rgFieldInfos = new CFieldInfo[m_nFields];
  3110. memset(m_rgFieldInfos, 0, sizeof(CFieldInfo) * m_nFields);
  3111. }
  3112. if (m_pbFieldFlags == NULL)
  3113. {
  3114. m_pbFieldFlags = new BYTE[m_nFields];
  3115. memset(m_pbFieldFlags, 0, m_nFields);
  3116. }
  3117. }
  3118. if (m_nParams != 0)
  3119. {
  3120. // Allocate buffers to hold param info
  3121. if (m_pbParamFlags == NULL)
  3122. {
  3123. m_pbParamFlags = new BYTE[m_nParams];
  3124. memset(m_pbParamFlags, 0, m_nParams);
  3125. }
  3126. if (m_plParamLength == NULL)
  3127. {
  3128. m_plParamLength = new LONG[m_nParams];
  3129. memset(m_plParamLength, 0, m_nParams*sizeof(LONG));
  3130. }
  3131. }
  3132. }
  3133. CATCH_ALL(e)
  3134. {
  3135. Close();
  3136. THROW_LAST();
  3137. }
  3138. END_CATCH_ALL
  3139. }
  3140. int CRecordset::GetBoundFieldIndex(void* pv)
  3141. {
  3142. void* pvIndex;
  3143. if (!m_mapFieldIndex.Lookup(pv, pvIndex))
  3144. return -1;
  3145. else
  3146. // Cached value is short not ptr
  3147. return (int)pvIndex;
  3148. }
  3149. int CRecordset::GetBoundParamIndex(void* pv)
  3150. {
  3151. void* pvIndex;
  3152. if (!m_mapParamIndex.Lookup(pv, pvIndex))
  3153. return -1;
  3154. else
  3155. // Cached value in data not ptr
  3156. return (int)pvIndex;
  3157. }
  3158. short CRecordset::GetFieldIndexByName(LPCTSTR lpszFieldName)
  3159. {
  3160. for (short nIndex = 0; nIndex < GetODBCFieldCount(); nIndex++)
  3161. {
  3162. if (m_rgODBCFieldInfos[nIndex].m_strName == lpszFieldName)
  3163. break;
  3164. }
  3165. // Check if field name found
  3166. if (nIndex == GetODBCFieldCount())
  3167. ThrowDBException(AFX_SQL_ERROR_FIELD_NOT_FOUND);
  3168. return nIndex;
  3169. }
  3170. long* CRecordset::GetFieldLengthBuffer(DWORD nField, int nFieldType)
  3171. {
  3172. if (nFieldType == CFieldExchange::outputColumn)
  3173. {
  3174. ASSERT(nField < m_nFields);
  3175. return &m_rgFieldInfos[nField].m_nLength;
  3176. }
  3177. else
  3178. {
  3179. ASSERT(nField < m_nParams);
  3180. return &m_plParamLength[nField];
  3181. }
  3182. }
  3183. BYTE CRecordset::GetFieldStatus(DWORD nField)
  3184. {
  3185. ASSERT(nField < m_nFields);
  3186. return m_pbFieldFlags[nField];
  3187. }
  3188. void CRecordset::SetFieldStatus(DWORD nField, BYTE bFlags)
  3189. {
  3190. ASSERT(nField < m_nFields);
  3191. m_pbFieldFlags[nField] |= bFlags;
  3192. }
  3193. void CRecordset::ClearFieldStatus()
  3194. {
  3195. memset(m_pbFieldFlags, 0, m_nFields);
  3196. }
  3197. BOOL CRecordset::IsFieldStatusDirty(DWORD nField) const
  3198. {
  3199. ASSERT(nField < m_nFields);
  3200. return m_pbFieldFlags[nField] & AFX_SQL_FIELD_FLAG_DIRTY;
  3201. }
  3202. void CRecordset::SetDirtyFieldStatus(DWORD nField)
  3203. {
  3204. ASSERT(nField < m_nFields);
  3205. m_pbFieldFlags[nField] |= AFX_SQL_FIELD_FLAG_DIRTY;
  3206. }
  3207. void CRecordset::ClearDirtyFieldStatus(DWORD nField)
  3208. {
  3209. ASSERT(nField < m_nFields);
  3210. m_pbFieldFlags[nField] &= ~AFX_SQL_FIELD_FLAG_DIRTY;
  3211. }
  3212. BOOL CRecordset::IsFieldStatusNull(DWORD nField) const
  3213. {
  3214. ASSERT(nField < m_nFields);
  3215. return m_pbFieldFlags[nField] & AFX_SQL_FIELD_FLAG_NULL;
  3216. }
  3217. void CRecordset::SetNullFieldStatus(DWORD nField)
  3218. {
  3219. ASSERT(nField < m_nFields);
  3220. m_pbFieldFlags[nField] |= AFX_SQL_FIELD_FLAG_NULL;
  3221. }
  3222. void CRecordset::ClearNullFieldStatus(DWORD nField)
  3223. {
  3224. ASSERT(nField < m_nFields);
  3225. m_pbFieldFlags[nField] &= ~AFX_SQL_FIELD_FLAG_NULL;
  3226. }
  3227. BOOL CRecordset::IsParamStatusNull(DWORD nParam) const
  3228. {
  3229. ASSERT(nParam < m_nParams);
  3230. return m_pbParamFlags[nParam] & AFX_SQL_FIELD_FLAG_NULL;
  3231. }
  3232. void CRecordset::SetNullParamStatus(DWORD nParam)
  3233. {
  3234. ASSERT(nParam < m_nParams);
  3235. m_pbParamFlags[nParam] |= AFX_SQL_FIELD_FLAG_NULL;
  3236. }
  3237. void CRecordset::ClearNullParamStatus(DWORD nParam)
  3238. {
  3239. ASSERT(nParam < m_nParams);
  3240. m_pbParamFlags[nParam] &= ~AFX_SQL_FIELD_FLAG_NULL;
  3241. }
  3242. BOOL CRecordset::IsFieldNullable(DWORD nField) const
  3243. {
  3244. ASSERT(nField <= INT_MAX);
  3245. ASSERT((long)nField < GetODBCFieldCount());
  3246. // return TRUE if nulls allowed or if not known
  3247. return m_rgODBCFieldInfos[nField].m_nNullability != SQL_NO_NULLS;
  3248. }
  3249. UINT CRecordset::BindParams(HSTMT hstmt)
  3250. {
  3251. ASSERT_VALID(this);
  3252. ASSERT(m_hstmt != SQL_NULL_HSTMT);
  3253. CFieldExchange fx(CFieldExchange::BindParam, this);
  3254. fx.m_hstmt = hstmt;
  3255. DoFieldExchange(&fx);
  3256. return fx.m_nParams;
  3257. }
  3258. void CRecordset::RebindParams(HSTMT hstmt)
  3259. {
  3260. ASSERT_VALID(this);
  3261. ASSERT(m_hstmt != SQL_NULL_HSTMT);
  3262. if (m_bRebindParams)
  3263. {
  3264. CFieldExchange fx(CFieldExchange::RebindParam, this);
  3265. fx.m_hstmt = hstmt;
  3266. DoFieldExchange(&fx);
  3267. }
  3268. }
  3269. UINT CRecordset::BindFieldsToColumns()
  3270. {
  3271. ASSERT_VALID(this);
  3272. ASSERT(m_hstmt != SQL_NULL_HSTMT);
  3273. ASSERT(m_nFieldsBound == 0);
  3274. ASSERT(m_nFields != 0 && m_nFields <= 255);
  3275. CFieldExchange fx(CFieldExchange::BindFieldToColumn, this);
  3276. fx.m_hstmt = m_hstmt;
  3277. // Binding depends on fetch type
  3278. if (m_dwOptions & useMultiRowFetch)
  3279. DoBulkFieldExchange(&fx);
  3280. else
  3281. DoFieldExchange(&fx);
  3282. return fx.m_nFields;
  3283. }
  3284. void CRecordset::BindFieldsForUpdate()
  3285. {
  3286. ASSERT_VALID(this);
  3287. if (m_nEditMode == edit || m_nEditMode == addnew)
  3288. {
  3289. CFieldExchange fx(CFieldExchange::BindFieldForUpdate, this);
  3290. fx.m_hstmt = m_hstmt;
  3291. DoFieldExchange(&fx);
  3292. }
  3293. }
  3294. void CRecordset::UnbindFieldsForUpdate()
  3295. {
  3296. ASSERT_VALID(this);
  3297. if (m_nEditMode == edit || m_nEditMode == addnew)
  3298. {
  3299. CFieldExchange fx(CFieldExchange::UnbindFieldForUpdate, this);
  3300. fx.m_hstmt = m_hstmt;
  3301. DoFieldExchange(&fx);
  3302. }
  3303. }
  3304. // After Move operation, reflect status and lengths of columns in RFX fields
  3305. void CRecordset::Fixups()
  3306. {
  3307. ASSERT_VALID(this);
  3308. ASSERT(m_hstmt != SQL_NULL_HSTMT);
  3309. ASSERT(m_nFieldsBound != 0);
  3310. CFieldExchange fx(CFieldExchange::Fixup, this);
  3311. fx.m_hstmt = m_hstmt;
  3312. DoFieldExchange(&fx);
  3313. }
  3314. UINT CRecordset::AppendNames(CString* pstr, LPCTSTR lpszSeparator)
  3315. {
  3316. ASSERT_VALID(this);
  3317. ASSERT(AfxIsValidAddress(pstr, sizeof(CString)));
  3318. ASSERT(AfxIsValidString(lpszSeparator));
  3319. ASSERT(m_hstmt != SQL_NULL_HSTMT);
  3320. CFieldExchange fx(CFieldExchange::Name, this);
  3321. fx.m_pstr = pstr;
  3322. fx.m_lpszSeparator = lpszSeparator;
  3323. if (m_dwOptions & useMultiRowFetch)
  3324. DoBulkFieldExchange(&fx);
  3325. else
  3326. DoFieldExchange(&fx);
  3327. return fx.m_nFields;
  3328. }
  3329. // For each "changed" column, append <column name>=<column value>,
  3330. UINT CRecordset::AppendNamesValues(HSTMT hstmt, CString* pstr,
  3331. LPCTSTR lpszSeparator)
  3332. {
  3333. ASSERT_VALID(this);
  3334. ASSERT(AfxIsValidAddress(pstr, sizeof(CString)));
  3335. ASSERT(AfxIsValidString(lpszSeparator));
  3336. ASSERT(m_hstmt != SQL_NULL_HSTMT);
  3337. ASSERT(hstmt != SQL_NULL_HSTMT);
  3338. CFieldExchange fx(CFieldExchange::NameValue, this);
  3339. fx.m_pstr = pstr;
  3340. fx.m_lpszSeparator = lpszSeparator;
  3341. fx.m_hstmt = hstmt;
  3342. DoFieldExchange(&fx);
  3343. return fx.m_nFields;
  3344. }
  3345. // For each "changed" column, append <column value>,
  3346. UINT CRecordset::AppendValues(HSTMT hstmt, CString* pstr,
  3347. LPCTSTR lpszSeparator)
  3348. {
  3349. ASSERT_VALID(this);
  3350. ASSERT(AfxIsValidAddress(pstr, sizeof(CString)));
  3351. ASSERT(AfxIsValidString(lpszSeparator));
  3352. ASSERT(m_hstmt != SQL_NULL_HSTMT);
  3353. ASSERT(hstmt != SQL_NULL_HSTMT);
  3354. CFieldExchange fx(CFieldExchange::Value, this);
  3355. fx.m_pstr = pstr;
  3356. fx.m_lpszSeparator = lpszSeparator;
  3357. fx.m_hstmt = hstmt;
  3358. DoFieldExchange(&fx);
  3359. return fx.m_nFields;
  3360. }
  3361. // Cache fields of copy buffer in a CMemFile with CArchive
  3362. void CRecordset::StoreFields()
  3363. {
  3364. ASSERT_VALID(this);
  3365. ASSERT(m_nFieldsBound != 0);
  3366. CFieldExchange fx(CFieldExchange::StoreField, this);
  3367. DoFieldExchange(&fx);
  3368. }
  3369. // Restore fields of copy buffer from archived memory file
  3370. void CRecordset::LoadFields()
  3371. {
  3372. ASSERT_VALID(this);
  3373. ASSERT(m_nFieldsBound != 0);
  3374. // Must clear out the old status
  3375. ClearFieldStatus();
  3376. CFieldExchange fx(CFieldExchange::LoadField, this);
  3377. DoFieldExchange(&fx);
  3378. }
  3379. void CRecordset::MarkForUpdate()
  3380. {
  3381. ASSERT_VALID(this);
  3382. CFieldExchange fx(CFieldExchange::MarkForUpdate, this);
  3383. DoFieldExchange(&fx);
  3384. }
  3385. void CRecordset::MarkForAddNew()
  3386. {
  3387. ASSERT_VALID(this);
  3388. CFieldExchange fx(CFieldExchange::MarkForAddNew, this);
  3389. DoFieldExchange(&fx);
  3390. }
  3391. void CRecordset::AllocDataCache()
  3392. {
  3393. ASSERT_VALID(this);
  3394. CFieldExchange fx(CFieldExchange::AllocCache, this);
  3395. DoFieldExchange(&fx);
  3396. }
  3397. void CRecordset::FreeDataCache()
  3398. {
  3399. ASSERT_VALID(this);
  3400. CFieldInfo* pInfo;
  3401. for (DWORD nField = 0; nField < m_nFields; nField++)
  3402. {
  3403. pInfo = &m_rgFieldInfos[nField];
  3404. switch(pInfo->m_nDataType)
  3405. {
  3406. default:
  3407. ASSERT(FALSE);
  3408. // fall through
  3409. // Data not cached
  3410. case AFX_RFX_NO_TYPE:
  3411. break;
  3412. // Types cached by value (sizeof(TYPE) <= sizeof(void*))
  3413. case AFX_RFX_BOOL:
  3414. case AFX_RFX_BYTE:
  3415. case AFX_RFX_INT:
  3416. case AFX_RFX_LONG:
  3417. case AFX_RFX_SINGLE:
  3418. pInfo->m_pvDataCache = NULL;
  3419. break;
  3420. case AFX_RFX_TEXT:
  3421. delete (CString*)pInfo->m_pvDataCache;
  3422. pInfo->m_pvDataCache = NULL;
  3423. break;
  3424. case AFX_RFX_LPTSTR:
  3425. delete [] LPTSTR(pInfo->m_pvDataCache);
  3426. pInfo->m_pvDataCache = NULL;
  3427. break;
  3428. case AFX_RFX_DOUBLE:
  3429. delete (double*)pInfo->m_pvDataCache;
  3430. pInfo->m_pvDataCache = NULL;
  3431. break;
  3432. case AFX_RFX_TIMESTAMP:
  3433. delete (TIMESTAMP_STRUCT*)pInfo->m_pvDataCache;
  3434. pInfo->m_pvDataCache = NULL;
  3435. break;
  3436. case AFX_RFX_OLEDATE:
  3437. delete (COleDateTime*)pInfo->m_pvDataCache;
  3438. pInfo->m_pvDataCache = NULL;
  3439. break;
  3440. case AFX_RFX_DATE:
  3441. delete (CTime*)pInfo->m_pvDataCache;
  3442. pInfo->m_pvDataCache = NULL;
  3443. break;
  3444. case AFX_RFX_BINARY:
  3445. delete (CByteArray*)pInfo->m_pvDataCache;
  3446. pInfo->m_pvDataCache = NULL;
  3447. break;
  3448. }
  3449. }
  3450. }
  3451. #ifdef _DEBUG
  3452. void CRecordset::DumpFields(CDumpContext& dc) const
  3453. {
  3454. CFieldExchange fx(CFieldExchange::DumpField, (CRecordset *)this);
  3455. fx.m_pdcDump = &dc;
  3456. ((CRecordset *)this)->DoFieldExchange(&fx);
  3457. }
  3458. #endif // _DEBUG
  3459. // Perform Update (m_nModeEdit == edit), Insert (addnew),
  3460. // or Delete (noMode)
  3461. BOOL CRecordset::UpdateInsertDelete()
  3462. {
  3463. ASSERT_VALID(this);
  3464. ASSERT(m_hstmt != SQL_NULL_HSTMT);
  3465. // Delete mode
  3466. if (m_nEditMode == addnew)
  3467. {
  3468. if (!m_bAppendable)
  3469. {
  3470. TRACE0("Error: attempted to add a record to a read only recordset.\n");
  3471. ThrowDBException(AFX_SQL_ERROR_RECORDSET_READONLY);
  3472. }
  3473. }
  3474. else
  3475. {
  3476. if (!m_bUpdatable)
  3477. {
  3478. TRACE0("Error: attempted to update a read only recordset.\n");
  3479. ThrowDBException(AFX_SQL_ERROR_RECORDSET_READONLY);
  3480. }
  3481. // Requires currency
  3482. if (m_bEOF || m_bBOF || m_bDeleted)
  3483. {
  3484. TRACE0("Error: attempting to update recordset - but no record is current.\n");
  3485. ThrowDBException(AFX_SQL_ERROR_NO_CURRENT_RECORD);
  3486. }
  3487. }
  3488. // Update or AddNew is NOP w/o at least 1 changed field
  3489. if (m_nEditMode != noMode && !IsFieldDirty(NULL))
  3490. return FALSE;
  3491. if (!m_bUseUpdateSQL)
  3492. {
  3493. // Most efficient update method
  3494. ExecuteSetPosUpdate();
  3495. }
  3496. else
  3497. {
  3498. BOOL bNullHstmt = (m_hstmtUpdate == NULL);
  3499. // Make sure m_hstmtUpdate allocated
  3500. PrepareUpdateHstmt();
  3501. // Build update SQL unless optimizing bulk adds and hstmt not NULL
  3502. if(!(m_dwOptions & optimizeBulkAdd) || bNullHstmt)
  3503. {
  3504. // Mark as first bulk add if optimizing
  3505. if(m_dwOptions & optimizeBulkAdd)
  3506. {
  3507. m_dwOptions &= ~optimizeBulkAdd;
  3508. m_dwOptions |= firstBulkAdd;
  3509. }
  3510. BuildUpdateSQL();
  3511. // Reset flag marking first optimization
  3512. if(m_dwOptions & firstBulkAdd)
  3513. {
  3514. m_dwOptions &= ~firstBulkAdd;
  3515. m_dwOptions |= optimizeBulkAdd;
  3516. }
  3517. }
  3518. else
  3519. {
  3520. // Just reset the data lengths and datetime proxies
  3521. AppendValues(m_hstmtUpdate, &m_strUpdateSQL, _afxComma);
  3522. }
  3523. ExecuteUpdateSQL();
  3524. }
  3525. TRY
  3526. {
  3527. // Delete
  3528. switch (m_nEditMode)
  3529. {
  3530. case noMode:
  3531. // Decrement record count
  3532. if (m_lCurrentRecord >= 0)
  3533. {
  3534. if (m_lRecordCount > 0)
  3535. m_lRecordCount--;
  3536. m_lCurrentRecord--;
  3537. }
  3538. // indicate on a deleted record
  3539. m_bDeleted = TRUE;
  3540. // Set all fields to NULL
  3541. SetFieldNull(NULL);
  3542. break;
  3543. case addnew:
  3544. // The recordset may no longer be empty (depending on driver behavior)
  3545. // reset m_bEOF/m_bBOF so Move can be called
  3546. m_bEOF = m_bBOF = FALSE;
  3547. if (m_pDatabase->m_bIncRecordCountOnAdd && m_lCurrentRecord >= 0)
  3548. {
  3549. if (m_lRecordCount != -1)
  3550. m_lRecordCount++;
  3551. m_lCurrentRecord++;
  3552. }
  3553. // Reset the data cache if necessary
  3554. if (m_bCheckCacheForDirtyFields && m_nFields > 0)
  3555. LoadFields();
  3556. break;
  3557. case edit:
  3558. break;
  3559. }
  3560. // Reset the edit mode
  3561. m_nEditMode = noMode;
  3562. }
  3563. END_TRY
  3564. // Unless doing a bulk AddNew, reset the dirty flags
  3565. if (m_bCheckCacheForDirtyFields && !(m_dwOptions & optimizeBulkAdd))
  3566. SetFieldDirty(NULL, FALSE);
  3567. // Must return TRUE since record updated
  3568. return TRUE;
  3569. }
  3570. // Fetch and alloc algorithms for CLongBinary data when length unknown
  3571. long CRecordset::GetLBFetchSize(long lOldSize)
  3572. {
  3573. // Make it twice as big
  3574. return lOldSize << 1;
  3575. }
  3576. long CRecordset::GetLBReallocSize(long lOldSize)
  3577. {
  3578. // Make it twice as big (no effect if less than fetch size)
  3579. return lOldSize << 1;
  3580. }
  3581. short PASCAL CRecordset::GetDefaultFieldType(short nSQLType)
  3582. {
  3583. short nFieldType = 0;
  3584. switch (nSQLType)
  3585. {
  3586. case SQL_BIT:
  3587. nFieldType = SQL_C_BIT;
  3588. break;
  3589. case SQL_TINYINT:
  3590. nFieldType = SQL_C_UTINYINT;
  3591. break;
  3592. case SQL_SMALLINT:
  3593. nFieldType = SQL_C_SSHORT;
  3594. break;
  3595. case SQL_INTEGER:
  3596. nFieldType = SQL_C_SLONG;
  3597. break;
  3598. case SQL_REAL:
  3599. nFieldType = SQL_C_FLOAT;
  3600. break;
  3601. case SQL_FLOAT:
  3602. case SQL_DOUBLE:
  3603. nFieldType = SQL_C_DOUBLE;
  3604. break;
  3605. case SQL_DATE:
  3606. case SQL_TIME:
  3607. case SQL_TIMESTAMP:
  3608. nFieldType = SQL_C_TIMESTAMP;
  3609. break;
  3610. case SQL_NUMERIC:
  3611. case SQL_DECIMAL:
  3612. case SQL_BIGINT:
  3613. case SQL_CHAR:
  3614. case SQL_VARCHAR:
  3615. case SQL_LONGVARCHAR:
  3616. nFieldType = SQL_C_CHAR;
  3617. break;
  3618. case SQL_BINARY:
  3619. case SQL_VARBINARY:
  3620. case SQL_LONGVARBINARY:
  3621. nFieldType = SQL_C_BINARY;
  3622. break;
  3623. default:
  3624. ASSERT(FALSE);
  3625. }
  3626. return nFieldType;
  3627. }
  3628. void* PASCAL CRecordset::GetDataBuffer(CDBVariant& varValue,
  3629. short nFieldType, int* pnLen, short nSQLType, UDWORD nPrecision)
  3630. {
  3631. void* pvData = NULL;
  3632. switch (nFieldType)
  3633. {
  3634. case SQL_C_BIT:
  3635. pvData = &varValue.m_boolVal;
  3636. varValue.m_dwType = DBVT_BOOL;
  3637. *pnLen = sizeof(varValue.m_boolVal);
  3638. break;
  3639. case SQL_C_UTINYINT:
  3640. pvData = &varValue.m_chVal;
  3641. varValue.m_dwType = DBVT_UCHAR;
  3642. *pnLen = sizeof(varValue.m_chVal);
  3643. break;
  3644. case SQL_C_SSHORT:
  3645. pvData = &varValue.m_iVal;
  3646. varValue.m_dwType = DBVT_SHORT;
  3647. *pnLen = sizeof(varValue.m_iVal);
  3648. break;
  3649. case SQL_C_SLONG:
  3650. pvData = &varValue.m_lVal;
  3651. varValue.m_dwType = DBVT_LONG;
  3652. *pnLen = sizeof(varValue.m_lVal);
  3653. break;
  3654. case SQL_C_FLOAT:
  3655. pvData = &varValue.m_fltVal;
  3656. varValue.m_dwType = DBVT_SINGLE;
  3657. *pnLen = sizeof(varValue.m_fltVal);
  3658. break;
  3659. case SQL_C_DOUBLE:
  3660. pvData = &varValue.m_dblVal;
  3661. varValue.m_dwType = DBVT_DOUBLE;
  3662. *pnLen = sizeof(varValue.m_dblVal);
  3663. break;
  3664. case SQL_C_TIMESTAMP:
  3665. pvData = varValue.m_pdate = new TIMESTAMP_STRUCT;
  3666. varValue.m_dwType = DBVT_DATE;
  3667. *pnLen = sizeof(*varValue.m_pdate);
  3668. break;
  3669. case SQL_C_CHAR:
  3670. varValue.m_pstring = new CString;
  3671. varValue.m_dwType = DBVT_STRING;
  3672. *pnLen = GetTextLen(nSQLType, nPrecision);
  3673. pvData = varValue.m_pstring->GetBufferSetLength(*pnLen);
  3674. break;
  3675. case SQL_C_BINARY:
  3676. varValue.m_pbinary = new CLongBinary;
  3677. varValue.m_dwType = DBVT_BINARY;
  3678. if (nSQLType == SQL_LONGVARBINARY)
  3679. {
  3680. // pvData can't be NULL, so nLen must be at least 1
  3681. *pnLen = 1;
  3682. }
  3683. else
  3684. {
  3685. // better know the length!
  3686. ASSERT(nPrecision != 0);
  3687. *pnLen = nPrecision;
  3688. }
  3689. varValue.m_pbinary->m_hData = ::GlobalAlloc(GMEM_MOVEABLE, *pnLen);
  3690. varValue.m_pbinary->m_dwDataLength = *pnLen;
  3691. pvData = ::GlobalLock(varValue.m_pbinary->m_hData);
  3692. break;
  3693. default:
  3694. ASSERT(FALSE);
  3695. }
  3696. return pvData;
  3697. }
  3698. int PASCAL CRecordset::GetTextLen(short nSQLType, UDWORD nPrecision)
  3699. {
  3700. int nLen;
  3701. if (nSQLType == SQL_LONGVARCHAR || nSQLType == SQL_LONGVARBINARY)
  3702. {
  3703. // Use a dummy length of 1 (will just get NULL terminator)
  3704. nLen = 1;
  3705. }
  3706. else
  3707. {
  3708. // better know the length
  3709. ASSERT(nPrecision >= 0);
  3710. nLen = nPrecision + 1;
  3711. // If converting Numeric or Decimal to text need
  3712. // room for decimal point and sign in string
  3713. if (nSQLType == SQL_NUMERIC || nSQLType == SQL_DECIMAL)
  3714. nLen += 2;
  3715. }
  3716. return nLen;
  3717. }
  3718. long PASCAL CRecordset::GetData(CDatabase* pdb, HSTMT hstmt,
  3719. short nFieldIndex, short nFieldType, LPVOID pvData, int nLen,
  3720. short nSQLType)
  3721. {
  3722. UNUSED(nSQLType);
  3723. long nActualSize;
  3724. RETCODE nRetCode;
  3725. // Retrieve the column in question
  3726. AFX_ODBC_CALL(::SQLGetData(hstmt, nFieldIndex,
  3727. nFieldType, pvData, nLen, &nActualSize));
  3728. // Ignore data truncated warnings for long data
  3729. if (nRetCode == SQL_SUCCESS_WITH_INFO)
  3730. {
  3731. #ifdef _DEBUG
  3732. CDBException e(nRetCode);
  3733. if (afxTraceFlags & traceDatabase)
  3734. {
  3735. CDBException e(nRetCode);
  3736. // Build the error string but don't send nuisance output to TRACE window
  3737. e.BuildErrorString(pdb, hstmt, FALSE);
  3738. // If not a data truncated warning on long var column,
  3739. // then send debug output
  3740. if ((nSQLType != SQL_LONGVARCHAR &&
  3741. nSQLType != SQL_LONGVARBINARY) ||
  3742. (e.m_strStateNativeOrigin.Find(_afxDataTruncated) < 0))
  3743. {
  3744. TRACE1("Warning: ODBC Success With Info on field %d.\n",
  3745. nFieldIndex - 1);
  3746. e.TraceErrorMessage(e.m_strError);
  3747. e.TraceErrorMessage(e.m_strStateNativeOrigin);
  3748. }
  3749. }
  3750. #endif // _DEBUG
  3751. }
  3752. else if (nRetCode == SQL_NO_DATA_FOUND)
  3753. {
  3754. TRACE0("Error: GetFieldValue operation failed on field %d.\n");
  3755. TRACE1("\tData already fetched for this field.\n",
  3756. nFieldIndex - 1);
  3757. AfxThrowDBException(nRetCode, pdb, hstmt);
  3758. }
  3759. else if (nRetCode != SQL_SUCCESS)
  3760. {
  3761. TRACE1("Error: GetFieldValue operation failed on field %d.\n",
  3762. nFieldIndex - 1);
  3763. AfxThrowDBException(nRetCode, pdb, hstmt);
  3764. }
  3765. return nActualSize;
  3766. }
  3767. void PASCAL CRecordset::GetLongCharDataAndCleanup(CDatabase* pdb,
  3768. HSTMT hstmt, short nFieldIndex, long nActualSize, LPVOID* ppvData,
  3769. int nLen, CString& strValue, short nSQLType)
  3770. {
  3771. RETCODE nRetCode;
  3772. // Release the buffer now that data has been fetched
  3773. strValue.ReleaseBuffer(nActualSize < nLen ? nActualSize : nLen);
  3774. // If long data, may need to call SQLGetData again
  3775. if (nLen <= nActualSize &&
  3776. (nSQLType == SQL_LONGVARCHAR || nSQLType == SQL_LONGVARBINARY))
  3777. {
  3778. // Reallocate the size (this will copy the data)
  3779. *ppvData = strValue.GetBufferSetLength(nActualSize + 1);
  3780. // Get pointer, skipping over original data, but not the NULL
  3781. int nOldLen = nLen - 1;
  3782. *ppvData = (BYTE*)*ppvData + nOldLen;
  3783. nLen = nActualSize + 1 - nOldLen;
  3784. // Retrieve the column in question
  3785. AFX_ODBC_CALL(::SQLGetData(hstmt, nFieldIndex,
  3786. SQL_C_CHAR, *ppvData, nLen, &nActualSize));
  3787. if (nRetCode == SQL_SUCCESS_WITH_INFO)
  3788. {
  3789. #ifdef _DEBUG
  3790. if (afxTraceFlags & traceDatabase)
  3791. {
  3792. TRACE1("Warning: ODBC Success With Info on field %d.\n",
  3793. nFieldIndex - 1);
  3794. CDBException e(nRetCode);
  3795. e.BuildErrorString(pdb, hstmt);
  3796. }
  3797. #endif // _DEBUG
  3798. }
  3799. else if (nRetCode != SQL_SUCCESS)
  3800. {
  3801. TRACE1("Error: GetFieldValue operation failed on field %d.\n",
  3802. nFieldIndex - 1);
  3803. AfxThrowDBException(nRetCode, pdb, hstmt);
  3804. }
  3805. // Release the buffer now that data has been fetched
  3806. strValue.ReleaseBuffer(nActualSize + nOldLen);
  3807. }
  3808. }
  3809. void PASCAL CRecordset::GetLongBinaryDataAndCleanup(CDatabase* pdb, HSTMT hstmt,
  3810. short nFieldIndex, long nActualSize, LPVOID* ppvData, int nLen,
  3811. CDBVariant& varValue, short nSQLType)
  3812. {
  3813. RETCODE nRetCode;
  3814. ::GlobalUnlock(varValue.m_pbinary->m_hData);
  3815. // If long data, may need to call SQLGetData again
  3816. if (nLen < nActualSize && nSQLType == SQL_LONGVARBINARY)
  3817. {
  3818. // Reallocate a bigger buffer
  3819. HGLOBAL hOldData = varValue.m_pbinary->m_hData;
  3820. varValue.m_pbinary->m_hData = ::GlobalReAlloc(hOldData,
  3821. nActualSize, GMEM_MOVEABLE);
  3822. // Validate the memory was allocated and can be locked
  3823. if (varValue.m_pbinary->m_hData == NULL)
  3824. {
  3825. // Restore the old handle (not NULL if Realloc failed)
  3826. varValue.m_pbinary->m_hData = hOldData;
  3827. AfxThrowMemoryException();
  3828. }
  3829. varValue.m_pbinary->m_dwDataLength = nActualSize;
  3830. // Get pointer, skipping over original data
  3831. *ppvData = (BYTE*)::GlobalLock(varValue.m_pbinary->m_hData) + nLen;
  3832. int nOldLen = nLen;
  3833. nLen = nActualSize - nOldLen;
  3834. // Retrieve the column in question
  3835. AFX_ODBC_CALL(::SQLGetData(hstmt, nFieldIndex,
  3836. SQL_C_BINARY, *ppvData, nLen, &nActualSize));
  3837. if (nRetCode == SQL_SUCCESS_WITH_INFO)
  3838. {
  3839. #ifdef _DEBUG
  3840. if (afxTraceFlags & traceDatabase)
  3841. {
  3842. TRACE1("Warning: ODBC Success With Info on field %d.\n",
  3843. nFieldIndex - 1);
  3844. CDBException e(nRetCode);
  3845. e.BuildErrorString(pdb, hstmt);
  3846. }
  3847. #endif // _DEBUG
  3848. }
  3849. else if (nRetCode != SQL_SUCCESS)
  3850. {
  3851. TRACE1("Error: GetFieldValue operation failed on field %d.\n",
  3852. nFieldIndex - 1);
  3853. AfxThrowDBException(nRetCode, pdb, hstmt);
  3854. }
  3855. ASSERT((int)varValue.m_pbinary->m_dwDataLength ==
  3856. nActualSize + nOldLen);
  3857. // Release the buffer now that data has been fetched
  3858. ::GlobalUnlock(varValue.m_pbinary->m_hData);
  3859. }
  3860. }
  3861. //////////////////////////////////////////////////////////////////////////////
  3862. // CRecordset diagnostics
  3863. #ifdef _DEBUG
  3864. void CRecordset::AssertValid() const
  3865. {
  3866. CObject::AssertValid();
  3867. if (m_pDatabase != NULL)
  3868. m_pDatabase->AssertValid();
  3869. }
  3870. void CRecordset::Dump(CDumpContext& dc) const
  3871. {
  3872. CObject::Dump(dc);
  3873. dc << "m_nOpenType = " << m_nOpenType;
  3874. dc << "\nm_strSQL = " << m_strSQL;
  3875. dc << "\nm_hstmt = " << m_hstmt;
  3876. dc << "\nm_bRecordsetDb = " << m_bRecordsetDb;
  3877. dc << "\nm_lOpen = " << m_lOpen;
  3878. dc << "\nm_bScrollable = " << m_bScrollable;
  3879. dc << "\nm_bUpdatable = " << m_bUpdatable;
  3880. dc << "\nm_bAppendable = " << m_bAppendable;
  3881. dc << "\nm_nFields = " << m_nFields;
  3882. dc << "\nm_nFieldsBound = " << m_nFieldsBound;
  3883. dc << "\nm_nParams = " << m_nParams;
  3884. dc << "\nm_bEOF = " << m_bEOF;
  3885. dc << "\nm_bBOF = " << m_bBOF;
  3886. dc << "\nm_bDeleted = " << m_bEOF;
  3887. dc << "\nm_bLockMode = " << m_nLockMode;
  3888. dc << "\nm_nEditMode = " << m_nEditMode;
  3889. dc << "\nm_strCursorName = " << m_strCursorName;
  3890. dc << "\nm_hstmtUpdate = " << m_hstmtUpdate;
  3891. dc << "\nDump values for each field in current record.";
  3892. DumpFields(dc);
  3893. if (dc.GetDepth() > 0)
  3894. {
  3895. if (m_pDatabase == NULL)
  3896. dc << "with no database\n";
  3897. else
  3898. dc << "with database: " << m_pDatabase;
  3899. }
  3900. }
  3901. #endif // _DEBUG
  3902. //////////////////////////////////////////////////////////////////////////////
  3903. // Helpers
  3904. void AFXAPI AfxSetCurrentRecord(long* plCurrentRecord, long nRows, RETCODE nRetCode)
  3905. {
  3906. if (*plCurrentRecord != AFX_CURRENT_RECORD_UNDEFINED &&
  3907. nRetCode != SQL_NO_DATA_FOUND)
  3908. *plCurrentRecord += nRows;
  3909. }
  3910. void AFXAPI AfxSetRecordCount(long* plRecordCount, long lCurrentRecord,
  3911. long nRows, BOOL bEOFSeen, RETCODE nRetCode)
  3912. {
  3913. // This function provided for backward binary compatibility
  3914. UNUSED(nRows); // not used in release build
  3915. ASSERT(nRows == 1);
  3916. AfxSetRecordCount(plRecordCount, lCurrentRecord, bEOFSeen, nRetCode);
  3917. }
  3918. void AFXAPI AfxSetRecordCount(long* plRecordCount, long lCurrentRecord,
  3919. BOOL bEOFSeen, RETCODE nRetCode)
  3920. {
  3921. // If not at the end and haven't yet been to the end, incr count
  3922. if (nRetCode != SQL_NO_DATA_FOUND && !bEOFSeen &&
  3923. lCurrentRecord != AFX_CURRENT_RECORD_UNDEFINED)
  3924. {
  3925. // lCurrentRecord 0-based and it's already been set
  3926. *plRecordCount =
  3927. __max(*plRecordCount, lCurrentRecord + 1);
  3928. }
  3929. }
  3930. //////////////////////////////////////////////////////////////////////////////
  3931. // Inline function declarations expanded out-of-line
  3932. #ifndef _AFX_ENABLE_INLINES
  3933. static char _szAfxDbInl[] = "afxdb.inl";
  3934. #undef THIS_FILE
  3935. #define THIS_FILE _szAfxDbInl
  3936. #define _AFXDBCORE_INLINE
  3937. #include "afxdb.inl"
  3938. #endif
  3939. #ifdef AFX_INIT_SEG
  3940. #pragma code_seg(AFX_INIT_SEG)
  3941. #endif
  3942. IMPLEMENT_DYNAMIC(CDBException, CException)
  3943. IMPLEMENT_DYNAMIC(CDatabase, CObject)
  3944. IMPLEMENT_DYNAMIC(CRecordset, CObject)
  3945. #pragma warning(disable: 4074)
  3946. #pragma init_seg(lib)
  3947. PROCESS_LOCAL(_AFX_DB_STATE, _afxDbState)
  3948. /////////////////////////////////////////////////////////////////////////////