DatabaseUtilities.cpp 15 KB

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