DatabaseUtilities.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. // DatabaseUtilites.cpp: implementation of the CDatabaseUtilites class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #include "stdafx.h"
  5. #include "CP_Main.h"
  6. #include "DatabaseUtilities.h"
  7. #include "ProcessPaste.h"
  8. #include <io.h>
  9. //////////////////////////////////////////////////////////////////////
  10. // Construction/Destruction
  11. //////////////////////////////////////////////////////////////////////
  12. BOOL CreateBackup(CString csPath)
  13. {
  14. CString csOriginal;
  15. int count = 0;
  16. // create a backup of the existing database
  17. do
  18. {
  19. count++;
  20. csOriginal = csPath + StrF(".%03d",count);
  21. // in case of some weird infinite loop
  22. if( count > 50 )
  23. {
  24. ASSERT(0);
  25. return FALSE;
  26. }
  27. } while( !::CopyFile(csPath, csOriginal, TRUE) );
  28. return TRUE;
  29. }
  30. CString GetDBName()
  31. {
  32. return CGetSetOptions::GetDBPath();
  33. }
  34. CString GetDefaultDBName()
  35. {
  36. CString csDefaultPath;
  37. LPMALLOC pMalloc;
  38. if(SUCCEEDED(::SHGetMalloc(&pMalloc)))
  39. {
  40. LPITEMIDLIST pidlPrograms;
  41. SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, &pidlPrograms);
  42. char string[MAX_PATH];
  43. SHGetPathFromIDList(pidlPrograms, string);
  44. pMalloc->Free(pidlPrograms);
  45. pMalloc->Release();
  46. csDefaultPath = string;
  47. csDefaultPath += "\\Ditto\\";
  48. if(_access(csDefaultPath, 0) == -1)
  49. CreateDirectory(csDefaultPath, NULL);
  50. csDefaultPath += DEFAULT_DB_NAME;
  51. }
  52. return csDefaultPath;
  53. }
  54. BOOL CheckDBExists(CString csDBPath)
  55. {
  56. if(_access(csDBPath, 0) == -1)
  57. {
  58. //Database didn't exist
  59. CGetSetOptions::SetDBPath("");
  60. // -- create a new one
  61. return CreateDB(GetDefaultDBName());
  62. }
  63. BOOL bRet = FALSE;
  64. int nRet = ValidDB(csDBPath);
  65. if(nRet == FALSE)
  66. {
  67. theApp.CloseDB();
  68. CGetSetOptions::SetDBPath("");
  69. //Db existed but was bad
  70. CString csMarkAsBad;
  71. csMarkAsBad = csDBPath;
  72. csMarkAsBad.Replace(".", "_BAD.");
  73. CString csPath = GetDefaultDBName();
  74. CString cs;
  75. cs.Format("Unrecognized Database Format \"%s\",\n"
  76. "the file will be renamed \"%s\",\n"
  77. "and a new database will be created,\n"
  78. "\"%s\"", csDBPath, csMarkAsBad, csPath);
  79. AfxMessageBox(cs);
  80. CFile::Rename(csDBPath, csMarkAsBad);
  81. bRet = CreateDB(csPath);
  82. }
  83. else if(nRet == ERROR_OPENING_DATABASE)
  84. bRet = ERROR_OPENING_DATABASE;
  85. else
  86. bRet = TRUE;
  87. return bRet;
  88. }
  89. // m_pErrorInfo:
  90. // - m_lErrorCode 0x00000cc1
  91. // - m_strSource "DAO.Fields"
  92. // - m_strDescription "Item not found in this collection."
  93. #define ON_FIELD_ABSENT(name,onabsent) \
  94. try { table.GetFieldInfo(name,info); } \
  95. catch(CDaoException* e) \
  96. { \
  97. if( !bUpgrade || e->m_pErrorInfo->m_lErrorCode != 0x00000cc1 ) \
  98. throw e; \
  99. if( bUpgraded == FALSE ) \
  100. CreateBackup(csPath); \
  101. bResult &= onabsent; \
  102. bUpgraded = TRUE; \
  103. e->Delete(); \
  104. }
  105. BOOL ValidDB(CString csPath, BOOL bUpgrade)
  106. {
  107. BOOL bResult = TRUE;
  108. BOOL bUpgraded = FALSE;
  109. try
  110. {
  111. CDaoDatabase db;
  112. try
  113. {
  114. db.Open(csPath);
  115. }
  116. catch(CDaoException* e)
  117. {
  118. TCHAR szErrorMessage[512];
  119. UINT nHelpContext;
  120. if(e->GetErrorMessage(szErrorMessage, 512, &nHelpContext))
  121. {
  122. if(strcmp(szErrorMessage, "Unable to initialize DAO/Jet db engine.") == 0)
  123. {
  124. e->Delete();
  125. return ERROR_OPENING_DATABASE;
  126. }
  127. }
  128. e->ReportError();
  129. e->Delete();
  130. }
  131. CDaoTableDef table(&db);
  132. CDaoFieldInfo info;
  133. table.Open("Main");
  134. table.GetFieldInfo("lID", info);
  135. table.GetFieldInfo("lDate", info);
  136. ON_FIELD_ABSENT("mText", Upgrade_mText(db)); // +mText, -strText, -strType
  137. table.GetFieldInfo("lShortCut", info);
  138. table.GetFieldInfo("lDontAutoDelete", info);
  139. table.GetFieldInfo("lTotalCopySize", info);
  140. ON_FIELD_ABSENT("bIsGroup", Upgrade_Groups(db));
  141. table.GetFieldInfo("lParentID", info); // part of Upgrade_Groups
  142. table.GetFieldInfo("dOrder", info); // part of Upgrade_Groups
  143. ON_FIELD_ABSENT("lDataID", Upgrade_ShareData(db)); // +lDataID, -lParentID
  144. table.Close();
  145. table.Open("Data");
  146. table.GetFieldInfo("lID", info);
  147. table.GetFieldInfo("lDataID", info); // part of Upgrade_ShareData()
  148. table.GetFieldInfo("strClipBoardFormat", info);
  149. table.GetFieldInfo("ooData", info);
  150. table.Close();
  151. table.Open("Types");
  152. table.GetFieldInfo("ID", info);
  153. table.GetFieldInfo("TypeText", info);
  154. table.Close();
  155. }
  156. catch(CDaoException* e)
  157. {
  158. e->ReportError();
  159. ASSERT(FALSE);
  160. e->Delete();
  161. return FALSE;
  162. }
  163. // if we upgraded, perform full validation again without upgrading
  164. if( bUpgraded )
  165. return ValidDB( csPath, FALSE);
  166. return bResult;
  167. }
  168. BOOL CreateDB(CString csPath)
  169. {
  170. try
  171. {
  172. CDaoDatabase db;
  173. EnsureDirectory(csPath);
  174. try
  175. {
  176. db.Create(csPath);
  177. }
  178. catch(CDaoException* e)
  179. {
  180. TCHAR szErrorMessage[512];
  181. UINT nHelpContext;
  182. if(e->GetErrorMessage(szErrorMessage, 512, &nHelpContext))
  183. {
  184. if(strcmp(szErrorMessage, "Unable to initialize DAO/Jet db engine.") == 0)
  185. {
  186. e->Delete();
  187. return ERROR_OPENING_DATABASE;
  188. }
  189. }
  190. e->ReportError();
  191. e->Delete();
  192. return FALSE;
  193. }
  194. CDaoTableDefEx table(&db);
  195. //Create the Main table
  196. table.Create("Main");
  197. table.CreateField("lID", dbLong, 4, dbAutoIncrField);
  198. table.CreateIndex(TRUE, "lID");
  199. table.CreateField("lDate", dbLong, 4, 0, "0");
  200. table.CreateIndex(FALSE, "lDate");
  201. table.CreateField("mText", dbMemo, 0, dbVariableField);
  202. table.CreateField("lShortCut", dbLong, 4, 0, "0");
  203. table.CreateIndex(FALSE, "lShortCut");
  204. table.CreateField("lDontAutoDelete", dbLong, 4, 0, "0");
  205. table.CreateField("lTotalCopySize", dbLong, 4, 0, "0");
  206. // GROUPS
  207. table.CreateField("bIsGroup", dbBoolean, 1, 0, "0"); // for Groups
  208. table.CreateIndex(FALSE, "bIsGroup");
  209. table.CreateField("lParentID", dbLong, 4, 0, "0"); // parent Group Main.lID
  210. table.CreateIndex(FALSE, "lParentID");
  211. table.CreateField("dOrder", dbDouble, 8, 0, "0"); // for Order within Groups
  212. table.CreateIndex(FALSE, "dOrder");
  213. // for sharing data amongst multiple clips
  214. table.CreateField("lDataID", dbLong, 4, 0, "0"); // corresponds to Data.lDataID
  215. table.CreateIndex(FALSE, "lDataID");
  216. table.Append();
  217. table.Close();
  218. //Create the Data Table
  219. table.Create("Data");
  220. table.CreateField("lID", dbLong, 4, dbAutoIncrField);
  221. table.CreateIndex(TRUE, "lID");
  222. table.CreateField("lDataID", dbLong, 4, 0, "0");
  223. table.CreateIndex(FALSE, "lDataID");
  224. table.CreateField("strClipBoardFormat", dbText, 50, dbVariableField);
  225. table.CreateField("ooData", dbLongBinary, 0);
  226. table.Append();
  227. table.Close();
  228. //Create the Types table
  229. table.Create("Types");
  230. table.CreateField("ID", dbLong, 4, dbAutoIncrField);
  231. table.CreateField("TypeText", dbText, 50, dbVariableField);
  232. table.Append();
  233. table.Close();
  234. db.Close();
  235. return TRUE;
  236. }
  237. catch(CDaoException *e)
  238. {
  239. e->ReportError();
  240. ASSERT(FALSE);
  241. e->Delete();
  242. }
  243. return FALSE;
  244. }
  245. // +mText, -strText, -strType
  246. BOOL Upgrade_mText(CDaoDatabase& db)
  247. {
  248. try
  249. {
  250. db.Execute("ALTER TABLE Main ADD COLUMN mText MEMO", dbFailOnError);
  251. db.Execute("UPDATE Main SET mText=strText", dbFailOnError);
  252. db.Execute("ALTER TABLE Main DROP COLUMN strText", dbFailOnError);
  253. db.Execute("ALTER TABLE Main DROP COLUMN strType", dbFailOnError);
  254. }
  255. CATCHDAO
  256. return TRUE;
  257. }
  258. BOOL Upgrade_Groups(CDaoDatabase& db)
  259. {
  260. try
  261. {
  262. CDaoTableDefEx table(&db);
  263. table.Open("Main");
  264. // Groups
  265. table.CreateField("bIsGroup", dbBoolean, 1, 0, "0"); // for Groups
  266. table.CreateIndex(FALSE, "bIsGroup");
  267. table.CreateField("lParentID", dbLong, 4, 0, "0"); // parent Group Main.lID
  268. table.CreateIndex(FALSE, "lParentID");
  269. table.CreateField("dOrder", dbDouble, 8, 0, "0"); // for Order within Groups
  270. table.CreateIndex(FALSE, "dOrder");
  271. table.Close();
  272. // set defaults (otherwise might be NULL)
  273. db.Execute("UPDATE Main SET bIsGroup = 0, lParentID = 0, dOrder = 0", dbFailOnError);
  274. }
  275. CATCHDAO
  276. return TRUE;
  277. }
  278. BOOL Upgrade_ShareData(CDaoDatabase& db)
  279. {
  280. CPopup status(10000,10000); // peg at the bottom-right corner of screen
  281. try
  282. {
  283. CDaoTableDefEx table(&db);
  284. table.Open("Main");
  285. table.CreateField("lDataID", dbLong, 4, 0, "0"); // corresponds to Data.lDataID
  286. table.CreateIndex(FALSE, "lDataID");
  287. table.Close();
  288. table.Open("Data");
  289. table.CreateField("lDataID", dbLong, 4, 0, "0"); // parent Group Main.lID
  290. table.CreateIndex(FALSE, "lDataID");
  291. table.Close();
  292. // set defaults
  293. db.Execute( "UPDATE Main SET lDataID = 0", dbFailOnError );
  294. db.Execute( "UPDATE Data SET lDataID = 0", dbFailOnError );
  295. // update Main.lDataID and Data.lParentID for sharing Data
  296. //
  297. // - multiple Formats (Data.lID) exist for a single ClipData (Data.lDataID)
  298. // - The value of lDataID is arbitrary, but must be unique to the ClipData.
  299. // - In order to ensure uniqueness, lDataID is assigned the lID of
  300. // the first Format in the Clip's set.
  301. COleVariant var((long)0);
  302. CDaoRecordset main(&db);
  303. long main_fldID;
  304. long main_fldDataID;
  305. long main_lID;
  306. CDaoRecordset data(&db);
  307. long data_fldID;
  308. long data_fldDataID;
  309. long lDataID;
  310. int count = 0;
  311. int i = 0;
  312. int percentPrev = -1;
  313. int percent = -1;
  314. main.Open(dbOpenDynaset,"SELECT lID, lDataID FROM Main");
  315. main_fldID = GetFieldPos(main,"lID");
  316. VERIFY(main_fldID == 0);
  317. main_fldDataID = GetFieldPos(main,"lDataID");
  318. VERIFY(main_fldDataID == 1);
  319. if( !main.IsEOF() )
  320. {
  321. main.MoveLast();
  322. count = main.GetRecordCount();
  323. main.MoveFirst();
  324. }
  325. // for each record in Main and its corresponding records in Data,
  326. // assign a new unique lDataID.
  327. while( !main.IsEOF() )
  328. {
  329. i++;
  330. percentPrev = percent;
  331. percent = (i*100)/count;
  332. if( percent != percentPrev )
  333. status.Show(StrF("Ditto: Upgrading database (%d%%)",percent));
  334. main.GetFieldValue(main_fldID,var);
  335. main_lID = var.lVal;
  336. data.Open(dbOpenDynaset, StrF(
  337. "SELECT lID, lDataID "
  338. "FROM Data WHERE lParentID = %d", main_lID) );
  339. data_fldID = GetFieldPos(data,"lID");
  340. VERIFY(data_fldID == 0);
  341. data_fldDataID = GetFieldPos(data,"lDataID");
  342. VERIFY(data_fldDataID == 1);
  343. // lDataID = the first data record lID
  344. lDataID = 0;
  345. if( !data.IsEOF() )
  346. {
  347. data.GetFieldValue(0,var); // 0 == lID field
  348. lDataID = var.lVal;
  349. }
  350. // assign all Data records the same lDataID
  351. while( !data.IsEOF() )
  352. {
  353. var.lVal = lDataID;
  354. data.Edit();
  355. data.SetFieldValue(1,var); // 1 == lDataID field
  356. data.Update();
  357. data.MoveNext();
  358. }
  359. // assign Main.lDataID
  360. var.lVal = lDataID;
  361. main.Edit();
  362. main.SetFieldValue(1,var); // 1 == lDataID field
  363. main.Update();
  364. main.MoveNext();
  365. data.Close();
  366. }
  367. main.Close();
  368. // delete the old field
  369. db.Execute("ALTER TABLE Data DROP CONSTRAINT lParentID", dbFailOnError);
  370. db.Execute("ALTER TABLE Data DROP COLUMN lParentID", dbFailOnError);
  371. }
  372. CATCHDAO
  373. return TRUE;
  374. }
  375. BOOL CompactDatabase()
  376. {
  377. if(!theApp.CloseDB())
  378. return FALSE;
  379. CString csDBName = GetDBName();
  380. CString csTempDBName = csDBName;
  381. csTempDBName.Replace(".mdb", "TempDBName.mdb");
  382. //Compact the database
  383. try
  384. {
  385. CDaoWorkspace::CompactDatabase(csDBName, csTempDBName);//, dbLangGeneral, 0, "andrew");//DATABASE_PASSWORD);
  386. }
  387. catch(CDaoException* e)
  388. {
  389. AfxMessageBox(e->m_pErrorInfo->m_strDescription);
  390. DeleteFile(csTempDBName);
  391. e->Delete();
  392. return FALSE;
  393. }
  394. catch(CMemoryException* e)
  395. {
  396. AfxMessageBox("Memory Exception");
  397. DeleteFile(csTempDBName);
  398. e->Delete();
  399. return FALSE;
  400. }
  401. //Since compacting the database creates a new db delete the old one and replace it
  402. //with the compacted db
  403. if(DeleteFile(csDBName))
  404. {
  405. try
  406. {
  407. CFile::Rename(csTempDBName, csDBName);
  408. }
  409. catch(CFileException *e)
  410. {
  411. e->ReportError();
  412. e->Delete();
  413. return FALSE;
  414. }
  415. }
  416. else
  417. AfxMessageBox("Error Compacting Database");
  418. return TRUE;
  419. }
  420. BOOL RepairDatabase()
  421. {
  422. if(!theApp.CloseDB())
  423. return FALSE;
  424. try
  425. {
  426. CDaoWorkspace::RepairDatabase(GetDBName());
  427. }
  428. catch(CDaoException *e)
  429. {
  430. AfxMessageBox(e->m_pErrorInfo->m_strDescription);
  431. e->Delete();
  432. return FALSE;
  433. }
  434. return TRUE;
  435. }
  436. BOOL RemoveOldEntries()
  437. {
  438. if(CGetSetOptions::GetCheckForMaxEntries())
  439. {
  440. long lMax = CGetSetOptions::GetMaxEntries();
  441. CMainTable recset;
  442. recset.Open("SELECT * FROM Main ORDER BY lDate DESC");
  443. if(!recset.IsEOF())
  444. {
  445. recset.MoveLast();
  446. long lCount = recset.GetRecordCount();
  447. CClipIDs IDs;
  448. while((lCount > lMax) && (!recset.IsBOF()))
  449. {
  450. //Only delete entries that have no shortcut and don't have the flag set
  451. if(recset.m_lShortCut == 0 && recset.m_lDontAutoDelete == 0)
  452. IDs.Add(recset.m_lID);
  453. lCount--;
  454. recset.MovePrev();
  455. }
  456. IDs.DeleteIDs();
  457. }
  458. }
  459. if(CGetSetOptions::GetCheckForExpiredEntries())
  460. {
  461. long lExpire = CGetSetOptions::GetExpiredEntries();
  462. if(lExpire)
  463. {
  464. CTime now = CTime::GetCurrentTime();
  465. now -= CTimeSpan(lExpire, 0, 0, 0);
  466. CMainTable recset;
  467. recset.Open("SELECT * FROM Main "
  468. "WHERE lDate < %d AND "
  469. "lShortCut = 0 AND lDontAutoDelete = 0", now.GetTime());
  470. CClipIDs IDs;
  471. while(!recset.IsEOF())
  472. {
  473. IDs.Add(recset.m_lID);
  474. recset.MoveNext();
  475. }
  476. IDs.DeleteIDs();
  477. }
  478. }
  479. return TRUE;
  480. }
  481. BOOL EnsureDirectory(CString csPath)
  482. {
  483. char drive[_MAX_DRIVE];
  484. char dir[_MAX_DIR];
  485. char fname[_MAX_FNAME];
  486. char ext[_MAX_EXT];
  487. _splitpath(csPath, drive, dir, fname, ext);
  488. CString csDir(drive);
  489. csDir += dir;
  490. if(_access(csDir, 0) == -1)
  491. {
  492. if(CreateDirectory(csDir, NULL))
  493. return TRUE;
  494. }
  495. else
  496. return TRUE;
  497. return FALSE;
  498. }
  499. BOOL ExecuteSQL( CString csSQL, BOOL bReportError, CDaoException** ppEx )
  500. {
  501. try
  502. {
  503. theApp.EnsureOpenDB();
  504. theApp.m_pDatabase->Execute(csSQL, dbFailOnError);
  505. }
  506. catch(CDaoException* e)
  507. {
  508. if( bReportError )
  509. e->ReportError();
  510. if( ppEx )
  511. *ppEx = e;
  512. else
  513. e->Delete();
  514. return FALSE;
  515. }
  516. return TRUE;
  517. }
  518. int GetFieldPos(CDaoRecordset& recs, LPCTSTR fieldName)
  519. {
  520. CDaoFieldInfo fi;
  521. int count = recs.GetFieldCount();
  522. for( int i = 0; i < count; i++ )
  523. {
  524. recs.GetFieldInfo(i, fi);
  525. if( fi.m_strName.Compare( fieldName ) == 0 )
  526. return i; // when found a match, return it
  527. }
  528. return -1;
  529. }
  530. void VerifyFieldPos(CDaoRecordset& recs, LPCTSTR fieldName, int index)
  531. {
  532. CDaoFieldInfo fi;
  533. int count = recs.GetFieldCount();
  534. VERIFY( index >= 0 && index < count );
  535. recs.GetFieldInfo(index, fi);
  536. VERIFY( fi.m_strName.Compare( fieldName ) == 0 );
  537. }
  538. CString GetFieldList(CDaoRecordset& recs)
  539. {
  540. CString field;
  541. CString list;
  542. CDaoFieldInfo fi;
  543. int count = recs.GetFieldCount();
  544. for( int i = 0; i < count; i++ )
  545. {
  546. recs.GetFieldInfo(i, fi);
  547. field = StrF("\n%d: ",i) + fi.m_strName;
  548. list += field;
  549. }
  550. return list;
  551. }