AutopatcherPostgreRepository.cpp 74 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166
  1. /*
  2. * Copyright (c) 2014, Oculus VR, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. */
  10. /// \file
  11. /// \brief An implementation of the AutopatcherRepositoryInterface to use PostgreSQL to store the relevant data
  12. #include "AutopatcherPostgreRepository.h"
  13. #include "AutopatcherPatchContext.h"
  14. #include "FileList.h"
  15. // libpq-fe.h is part of PostgreSQL which must be installed on this computer to use the PostgreRepository
  16. #include "libpq-fe.h"
  17. #include "CreatePatch.h"
  18. #include "AutopatcherPatchContext.h"
  19. // #include "DR_SHA1.h"
  20. #include <stdlib.h>
  21. #include "LinuxStrings.h"
  22. // localtime
  23. #include <time.h>
  24. static const unsigned HASH_LENGTH=sizeof(unsigned int);
  25. // ntohl
  26. #ifdef _WIN32
  27. #include <Winsock2.h>
  28. #else
  29. #include <netinet/in.h>
  30. #endif
  31. // alloca
  32. #ifdef _COMPATIBILITY_1
  33. #elif defined(_WIN32)
  34. #include <malloc.h>
  35. #else
  36. //#include <stdlib.h>
  37. #endif
  38. #define PQEXECPARAM_FORMAT_TEXT 0
  39. #define PQEXECPARAM_FORMAT_BINARY 1
  40. using namespace RakNet;
  41. AutopatcherPostgreRepository::AutopatcherPostgreRepository()
  42. {
  43. filePartConnection=0;
  44. }
  45. AutopatcherPostgreRepository::~AutopatcherPostgreRepository()
  46. {
  47. if (filePartConnection)
  48. PQfinish(filePartConnection);
  49. }
  50. bool AutopatcherPostgreRepository::CreateAutopatcherTables(void)
  51. {
  52. if (isConnected==false)
  53. return false;
  54. const char *command =
  55. "BEGIN;"
  56. "CREATE TABLE Applications ("
  57. "applicationID serial PRIMARY KEY UNIQUE,"
  58. "applicationName text NOT NULL UNIQUE,"
  59. "changeSetID integer NOT NULL DEFAULT 0,"
  60. "userName text NOT NULL"
  61. ");"
  62. "CREATE TABLE FileVersionHistory ("
  63. "fileID serial PRIMARY KEY UNIQUE,"
  64. "applicationID integer REFERENCES Applications ON DELETE CASCADE,"
  65. "filename text NOT NULL,"
  66. "fileLength integer,"
  67. "content bytea,"
  68. "contentHash bytea,"
  69. "patch bytea,"
  70. "patchAlgorithm integer,"
  71. "createFile boolean NOT NULL,"
  72. "modificationDate double precision DEFAULT (EXTRACT(EPOCH FROM now())),"
  73. "lastSentDate double precision,"
  74. "timesSent integer NOT NULL DEFAULT 0,"
  75. "changeSetID integer NOT NULL,"
  76. "userName text NOT NULL,"
  77. "CONSTRAINT file_has_data CHECK ( createFile=FALSE OR ((content IS NOT NULL) AND (contentHash IS NOT NULL) AND (fileLength IS NOT NULL) ) )"
  78. ");"
  79. "CREATE VIEW AutoPatcherView AS SELECT "
  80. "FileVersionHistory.applicationid,"
  81. "Applications.applicationName,"
  82. "FileVersionHistory.fileID,"
  83. "FileVersionHistory.fileName,"
  84. "FileVersionHistory.createFile,"
  85. "FileVersionHistory.fileLength,"
  86. "FileVersionHistory.changeSetID,"
  87. "FileVersionHistory.lastSentDate,"
  88. "FileVersionHistory.modificationDate,"
  89. "FileVersionHistory.timesSent "
  90. "FROM (FileVersionHistory JOIN Applications ON "
  91. "( FileVersionHistory.applicationID = Applications.applicationID )) "
  92. "ORDER BY Applications.applicationID ASC, FileVersionHistory.fileID ASC;"
  93. "COMMIT;";
  94. PGresult *result;
  95. //sqlCommandMutex.Lock();
  96. bool res = ExecuteBlockingCommand(command, &result, true);
  97. //sqlCommandMutex.Unlock();
  98. PQclear(result);
  99. return res;
  100. }
  101. bool AutopatcherPostgreRepository2::CreateAutopatcherTables(void)
  102. {
  103. if (isConnected==false)
  104. return false;
  105. const char *command =
  106. "BEGIN;"
  107. "CREATE TABLE Applications ("
  108. "applicationID serial PRIMARY KEY UNIQUE,"
  109. "applicationName text NOT NULL UNIQUE,"
  110. "changeSetID integer NOT NULL DEFAULT 0,"
  111. "userName text NOT NULL"
  112. ");"
  113. "CREATE TABLE FileVersionHistory ("
  114. "fileID serial PRIMARY KEY UNIQUE,"
  115. "applicationID integer REFERENCES Applications ON DELETE CASCADE,"
  116. "filename text NOT NULL,"
  117. "fileLength integer,"
  118. "pathToContent text,"
  119. "contentHash bytea,"
  120. "patch bytea,"
  121. "patchAlgorithm integer,"
  122. "createFile boolean NOT NULL,"
  123. "modificationDate double precision DEFAULT (EXTRACT(EPOCH FROM now())),"
  124. "lastSentDate double precision,"
  125. "timesSent integer NOT NULL DEFAULT 0,"
  126. "changeSetID integer NOT NULL,"
  127. "userName text NOT NULL,"
  128. "CONSTRAINT file_has_data CHECK ( createFile=FALSE OR ((contentHash IS NOT NULL) AND (fileLength IS NOT NULL) ) )"
  129. ");"
  130. "CREATE VIEW AutoPatcherView AS SELECT "
  131. "FileVersionHistory.applicationid,"
  132. "Applications.applicationName,"
  133. "FileVersionHistory.fileID,"
  134. "FileVersionHistory.fileName,"
  135. "FileVersionHistory.createFile,"
  136. "FileVersionHistory.fileLength,"
  137. "FileVersionHistory.changeSetID,"
  138. "FileVersionHistory.lastSentDate,"
  139. "FileVersionHistory.modificationDate,"
  140. "FileVersionHistory.timesSent "
  141. "FROM (FileVersionHistory JOIN Applications ON "
  142. "( FileVersionHistory.applicationID = Applications.applicationID )) "
  143. "ORDER BY Applications.applicationID ASC, FileVersionHistory.fileID ASC;"
  144. "COMMIT;";
  145. PGresult *result;
  146. //sqlCommandMutex.Lock();
  147. bool res = ExecuteBlockingCommand(command, &result, true);
  148. //sqlCommandMutex.Unlock();
  149. PQclear(result);
  150. return res;
  151. }
  152. bool AutopatcherPostgreRepository::DestroyAutopatcherTables(void)
  153. {
  154. if (isConnected==false)
  155. return false;
  156. const char *command =
  157. "BEGIN;"
  158. "DROP TABLE Applications CASCADE;"
  159. "DROP TABLE FileVersionHistory CASCADE;"
  160. "COMMIT;";
  161. PGresult *result;
  162. //sqlCommandMutex.Lock();
  163. bool b = ExecuteBlockingCommand(command, &result, true);
  164. //sqlCommandMutex.Unlock();
  165. PQclear(result);
  166. return b;
  167. }
  168. bool AutopatcherPostgreRepository::AddApplication(const char *applicationName, const char *userName)
  169. {
  170. char query[512];
  171. if (strlen(applicationName)>100)
  172. return false;
  173. if (strlen(userName)>100)
  174. return false;
  175. sprintf(query, "INSERT INTO Applications (applicationName, userName) VALUES ('%s', '%s');", GetEscapedString(applicationName).C_String(), GetEscapedString(userName).C_String());
  176. PGresult *result;
  177. //sqlCommandMutex.Lock();
  178. bool b = ExecuteBlockingCommand(query, &result, false);
  179. //sqlCommandMutex.Unlock();
  180. PQclear(result);
  181. return b;
  182. }
  183. bool AutopatcherPostgreRepository::RemoveApplication(const char *applicationName)
  184. {
  185. char query[512];
  186. if (strlen(applicationName)>100)
  187. return false;
  188. sprintf(query, "DELETE FROM Applications WHERE applicationName='%s';", GetEscapedString(applicationName).C_String());
  189. PGresult *result;
  190. //sqlCommandMutex.Lock();
  191. bool b = ExecuteBlockingCommand(query, &result, false);
  192. //sqlCommandMutex.Unlock();
  193. PQclear(result);
  194. return b;
  195. }
  196. bool AutopatcherPostgreRepository::GetChangelistSinceDate(const char *applicationName, FileList *addedOrModifiedFilesWithHashData, FileList *deletedFiles, double sinceDate)
  197. {
  198. PGresult *result;
  199. char query[512];
  200. if (strlen(applicationName)>100)
  201. return false;
  202. RakNet::RakString escapedApplicationName = GetEscapedString(applicationName);
  203. sprintf(query, "SELECT applicationID FROM applications WHERE applicationName='%s';", escapedApplicationName.C_String());
  204. //sqlCommandMutex.Lock();
  205. if (ExecuteBlockingCommand(query, &result, false)==false)
  206. {
  207. //sqlCommandMutex.Unlock();
  208. PQclear(result);
  209. return false;
  210. }
  211. //sqlCommandMutex.Unlock();
  212. int numRows;
  213. numRows = PQntuples(result);
  214. if (numRows==0)
  215. {
  216. sprintf(lastError,"ERROR: %s not found in UpdateApplicationFiles\n",applicationName);
  217. return false;
  218. }
  219. char *res;
  220. res = PQgetvalue(result,0,0);
  221. int applicationID;
  222. applicationID=atoi(res);
  223. PQclear(result);
  224. if (sinceDate!=0.0)
  225. sprintf(query, "SELECT DISTINCT ON (filename) filename, fileLength, contentHash, createFile FROM FileVersionHistory WHERE applicationId=%i AND modificationDate > %f ORDER BY filename, fileId DESC;", applicationID, sinceDate);
  226. else
  227. sprintf(query, "SELECT DISTINCT ON (filename) filename, fileLength, contentHash, createFile FROM FileVersionHistory WHERE applicationId=%i ORDER BY filename, fileId DESC;", applicationID);
  228. //sqlCommandMutex.Lock();
  229. result = PQexecParams(pgConn, query,0,0,0,0,0,PQEXECPARAM_FORMAT_BINARY);
  230. //sqlCommandMutex.Unlock();
  231. if (IsResultSuccessful(result, false)==false)
  232. {
  233. PQclear(result);
  234. return false;
  235. }
  236. int filenameColumnIndex = PQfnumber(result, "filename");
  237. int contentHashColumnIndex = PQfnumber(result, "contentHash");
  238. int createFileColumnIndex = PQfnumber(result, "createFile");
  239. int fileLengthColumnIndex = PQfnumber(result, "fileLength");
  240. char *hardDriveFilename;
  241. char *hardDriveHash;
  242. char *createFileResult;
  243. char *fileLengthPtr;
  244. int rowIndex;
  245. int fileLength;
  246. RakAssert(PQfsize(result, fileLengthColumnIndex)==sizeof(fileLength));
  247. numRows = PQntuples(result);
  248. for (rowIndex=0; rowIndex < numRows; rowIndex++)
  249. {
  250. createFileResult = PQgetvalue(result, rowIndex, createFileColumnIndex);
  251. hardDriveFilename = PQgetvalue(result, rowIndex, filenameColumnIndex);
  252. if (createFileResult[0]==1)
  253. {
  254. hardDriveHash = PQgetvalue(result, rowIndex, contentHashColumnIndex);
  255. fileLengthPtr = PQgetvalue(result, rowIndex, fileLengthColumnIndex);
  256. memcpy(&fileLength, fileLengthPtr, sizeof(fileLength));
  257. fileLength=ntohl(fileLength); // This is asinine...
  258. addedOrModifiedFilesWithHashData->AddFile(hardDriveFilename, hardDriveFilename, hardDriveHash, HASH_LENGTH, fileLength, FileListNodeContext(0,0,0,0), false);
  259. }
  260. else
  261. {
  262. deletedFiles->AddFile(hardDriveFilename,hardDriveFilename,0,0,0,FileListNodeContext(0,0,0,0), false);
  263. }
  264. }
  265. return true;
  266. }
  267. int AutopatcherPostgreRepository::GetPatches(const char *applicationName, FileList *input, bool allowDownloadOfOriginalUnmodifiedFiles, FileList *patchList)
  268. {
  269. PGresult *result;
  270. char query[512];
  271. if (strlen(applicationName)>100)
  272. return 0;
  273. RakNet::RakString escapedApplicationName = GetEscapedString(applicationName);
  274. sprintf(query, "SELECT applicationID FROM applications WHERE applicationName='%s';", escapedApplicationName.C_String());
  275. //sqlCommandMutex.Lock();
  276. if (ExecuteBlockingCommand(query, &result, false)==false)
  277. {
  278. //sqlCommandMutex.Unlock();
  279. PQclear(result);
  280. return 0;
  281. }
  282. //sqlCommandMutex.Unlock();
  283. int numRows;
  284. numRows = PQntuples(result);
  285. if (numRows==0)
  286. {
  287. sprintf(lastError,"ERROR: %s not found in UpdateApplicationFiles\n",applicationName);
  288. return 0;
  289. }
  290. char *res;
  291. res = PQgetvalue(result,0,0);
  292. int applicationID;
  293. applicationID=atoi(res);
  294. PQclear(result);
  295. // Go through the input list.
  296. unsigned inputIndex;
  297. char *userHash, *contentHash;
  298. RakNet::RakString userFilename;
  299. // char *content;
  300. // char *fileId, *fileLength;
  301. char *patch;
  302. // int contentLength;
  303. int patchLength;
  304. // int contentColumnIndex;
  305. int contentHashIndex, fileIdIndex, fileLengthIndex;
  306. const char *outTemp[2];
  307. int outLengths[2];
  308. int formats[2];
  309. PGresult *patchResult;
  310. for (inputIndex=0; inputIndex < input->fileList.Size(); inputIndex++)
  311. {
  312. userHash=input->fileList[inputIndex].data;
  313. userFilename=input->fileList[inputIndex].filename;
  314. if (userHash==0)
  315. {
  316. // If the user does not have a hash in the input list, get the contents of latest version of this named file and write it to the patch list
  317. // sprintf(query, "SELECT DISTINCT ON (filename) content FROM FileVersionHistory WHERE applicationId=%i AND filename=$1::text ORDER BY filename, fileId DESC;", applicationID);
  318. sprintf(query, "SELECT DISTINCT ON (filename) fileId, fileLength, changeSetID FROM FileVersionHistory WHERE applicationId=%i AND filename=$1::text ORDER BY filename, fileId DESC;", applicationID);
  319. outTemp[0]=userFilename.C_String();
  320. outLengths[0]=(int) userFilename.GetLength();
  321. formats[0]=PQEXECPARAM_FORMAT_BINARY;
  322. //sqlCommandMutex.Lock();
  323. result = PQexecParams(pgConn, query,1,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  324. //sqlCommandMutex.Unlock();
  325. if (IsResultSuccessful(result, false)==false)
  326. {
  327. PQclear(result);
  328. return 0;
  329. }
  330. numRows = PQntuples(result);
  331. if (numRows>0)
  332. {
  333. // content = PQgetvalue(result, 0, 0);
  334. // contentLength=PQgetlength(result, 0, 0);
  335. // patchList->AddFile(userFilename, content, contentLength, contentLength, FileListNodeContext(PC_WRITE_FILE,0),false);
  336. int fileIdIndex = PQfnumber(result, "fileId");
  337. int fileLengthIndex = PQfnumber(result, "fileLength");
  338. int changeSetIdIndex = PQfnumber(result, "changeSetID");
  339. int fileId = ntohl(*((int*)PQgetvalue(result, 0, fileIdIndex)));
  340. int fileLength = ntohl(*((int*)PQgetvalue(result, 0, fileLengthIndex)));
  341. int changeSetId = ntohl(*((int*)PQgetvalue(result, 0, changeSetIdIndex)));
  342. if (allowDownloadOfOriginalUnmodifiedFiles==false && changeSetId==0)
  343. {
  344. printf("Failure: allowDownloadOfOriginalUnmodifiedFiles==false for %s length %i\n", userFilename.C_String(), fileLength);
  345. PQclear(result);
  346. return -1;
  347. }
  348. patchList->AddFile(userFilename,userFilename, 0, fileLength, fileLength, FileListNodeContext(PC_WRITE_FILE,fileId,0,0),true);
  349. }
  350. PQclear(result);
  351. }
  352. else // Assuming the user does have a hash.
  353. {
  354. if (input->fileList[inputIndex].dataLengthBytes!=HASH_LENGTH)
  355. return 0;
  356. // Get the hash and ID of the latest version of this file, by filename.
  357. sprintf(query, "SELECT DISTINCT ON (filename) contentHash, fileId, fileLength FROM FileVersionHistory WHERE applicationId=%i AND filename=$1::text ORDER BY filename, fileId DESC;", applicationID);
  358. outTemp[0]=userFilename.C_String();
  359. outLengths[0]=(int)userFilename.GetLength();
  360. formats[0]=PQEXECPARAM_FORMAT_BINARY;
  361. //sqlCommandMutex.Lock();
  362. result = PQexecParams(pgConn, query,1,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  363. //sqlCommandMutex.Unlock();
  364. if (IsResultSuccessful(result, false)==false)
  365. {
  366. PQclear(result);
  367. return 0;
  368. }
  369. numRows = PQntuples(result);
  370. if (numRows>0)
  371. {
  372. contentHashIndex = PQfnumber(result, "contentHash");
  373. fileIdIndex = PQfnumber(result, "fileId");
  374. fileLengthIndex = PQfnumber(result, "fileLength");
  375. contentHash = PQgetvalue(result, 0, contentHashIndex);
  376. int fileIdInt = ntohl(*((int*)PQgetvalue(result, 0, fileIdIndex)));
  377. int fileLengthInt = ntohl(*((int*)PQgetvalue(result, 0, fileLengthIndex)));
  378. // fileId = PQgetvalue(result, 0, fileIdIndex);
  379. // fileLength = PQgetvalue(result, 0, fileLengthIndex);
  380. // int fileLengthInt = ntohl(*((int*)fileLength));
  381. // unsigned int hash1 = SuperFastHashFile("C:/temp/RakNet_Icon_Final.psd");
  382. // unsigned int hash2 = SuperFastHashFile("C:/temp/AutopatcherClient/RakNet_Icon_Final.psd");
  383. if (memcmp(contentHash, userHash, HASH_LENGTH)!=0)
  384. {
  385. // Look up by user hash/filename/applicationID, returning the patch
  386. sprintf(query, "SELECT patch, patchAlgorithm, fileId FROM FileVersionHistory WHERE applicationId=%i AND filename=$1::text AND contentHash=$2::bytea;", applicationID);
  387. outTemp[0]=userFilename.C_String();
  388. outLengths[0]=(int)userFilename.GetLength();
  389. formats[0]=PQEXECPARAM_FORMAT_TEXT;
  390. outTemp[1]=userHash;
  391. outLengths[1]=HASH_LENGTH;
  392. formats[1]=PQEXECPARAM_FORMAT_BINARY;
  393. //sqlCommandMutex.Lock();
  394. patchResult = PQexecParams(pgConn, query,2,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  395. //sqlCommandMutex.Unlock();
  396. if (IsResultSuccessful(patchResult, false)==false)
  397. {
  398. PQclear(patchResult);
  399. return 0;
  400. }
  401. numRows = PQntuples(patchResult);
  402. if (numRows==0)
  403. {
  404. PQclear(patchResult);
  405. // outTemp[0]=fileId;
  406. // outLengths[0]=PQfsize(result, fileIdIndex);
  407. // formats[0]=PQEXECPARAM_FORMAT_BINARY;
  408. /*
  409. // Get 32000000 bytes at a time to workaround http://support.microsoft.com/kb/q201213
  410. int fileIndex=0;
  411. char *file = RakNet::OP_NEW char[fileLengthInt];
  412. while (fileIndex < fileLengthInt)
  413. {
  414. sprintf(query, "SELECT substring(content from %i for 32000000) FROM FileVersionHistory WHERE fileId=$1::integer;", fileIndex, fileId);
  415. patchResult = PQexecParams(pgConn, query,1,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  416. if (IsResultSuccessful(patchResult, false)==false)
  417. {
  418. PQclear(patchResult);
  419. return false;
  420. }
  421. content = PQgetvalue(patchResult, 0, 0);
  422. contentLength=PQgetlength(patchResult, 0, 0);
  423. assert(contentLength==32000000 || contentLength==fileLengthInt-fileIndex);
  424. memcpy(file+fileIndex,content,contentLength);
  425. PQclear(patchResult);
  426. fileIndex+=32000000;
  427. }
  428. */
  429. /*
  430. sprintf(query, "SELECT content FROM FileVersionHistory WHERE fileId=%i", fileIdInt);
  431. patchResult = PQexecParams(pgConn, query,0,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  432. if (IsResultSuccessful(patchResult, false)==false)
  433. {
  434. PQclear(patchResult);
  435. return false;
  436. }
  437. char *content = PQgetvalue(patchResult, 0, 0);
  438. int contentLength=PQgetlength(patchResult, 0, 0);
  439. unsigned int hash1 = SuperFastHashFile("C:/temp/RakNet_Icon_Final.psd");
  440. unsigned int hash2 = SuperFastHash(content, contentLength);
  441. */
  442. // No patch, add the file
  443. patchList->AddFile(userFilename,userFilename, 0, fileLengthInt, fileLengthInt, FileListNodeContext(PC_WRITE_FILE,fileIdInt,0,0), true);
  444. // patchList->AddFile(userFilename, file, fileLengthInt, contentLength, FileListNodeContext(PC_WRITE_FILE,0), true);
  445. // RakNet::OP_DELETE_ARRAY file;
  446. }
  447. else
  448. {
  449. int patchColumnIndex = PQfnumber(patchResult, "patch");
  450. int patchAlgorithmColumnIndex = PQfnumber(patchResult, "patchAlgorithm");
  451. // Otherwise, write the hash of the new version and then write the patch to get to that version.
  452. //
  453. // int patchAlgorithm = ntohl(*((int*)PQgetvalue(result, 0, patchAlgorithmColumnIndex)));
  454. const char *tv = PQgetvalue(patchResult, 0, patchAlgorithmColumnIndex);
  455. int patchAlgorithm;
  456. if (tv)
  457. patchAlgorithm = ntohl(*((int*)tv));
  458. else
  459. patchAlgorithm = 0;
  460. int patchFileIdColumnIndex = PQfnumber(patchResult, "fileId");
  461. const char *tv2 = PQgetvalue(patchResult, 0, patchFileIdColumnIndex);
  462. int patchIdInt = ntohl(*((int*)tv2));
  463. patchLength=PQgetlength(patchResult, 0, patchColumnIndex);
  464. bool useReference = patchLength > 1048576;
  465. char *temp;
  466. if (useReference==false)
  467. temp = RakNet::OP_NEW_ARRAY<char>(patchLength + HASH_LENGTH, _FILE_AND_LINE_ );
  468. else
  469. temp = 0;
  470. if (temp!=0)
  471. {
  472. patch = PQgetvalue(patchResult, 0, patchColumnIndex);
  473. memcpy(temp, contentHash, HASH_LENGTH);
  474. memcpy(temp+HASH_LENGTH, patch, patchLength);
  475. // int len;
  476. // assert(PQfsize(result, fileLengthIndex)==sizeof(fileLength));
  477. // memcpy(&len, fileLength, sizeof(len));
  478. // len=ntohl(len);
  479. // printf("send patch %i bytes\n", patchLength);
  480. // for (int i=0; i < patchLength; i++)
  481. // printf("%i ", patch[i]);
  482. // printf("\n");
  483. patchList->AddFile(userFilename,userFilename, temp, HASH_LENGTH+patchLength, fileLengthInt, FileListNodeContext(PC_HASH_1_WITH_PATCH,0,patchAlgorithm,0),false );
  484. }
  485. else
  486. {
  487. patchList->AddFile(userFilename,userFilename, temp, HASH_LENGTH+patchLength, fileLengthInt, FileListNodeContext(PC_HASH_1_WITH_PATCH,fileIdInt,patchAlgorithm,patchIdInt),true );
  488. }
  489. PQclear(patchResult);
  490. RakNet::OP_DELETE_ARRAY(temp, _FILE_AND_LINE_);
  491. }
  492. }
  493. else
  494. {
  495. // else if the hash of this file matches what the user has, the user has the latest version. Done.
  496. }
  497. }
  498. else
  499. {
  500. // else if there is no such file, skip this file.
  501. }
  502. PQclear(result);
  503. }
  504. }
  505. return 1;
  506. }
  507. bool AutopatcherPostgreRepository::GetMostRecentChangelistWithPatches(RakNet::RakString &applicationName, FileList *patchedFiles, FileList *addedFiles, FileList *addedOrModifiedFileHashes, FileList *deletedFiles, double *priorRowPatchTime, double *mostRecentRowPatchTime)
  508. {
  509. PGresult *result;
  510. char query[1024];
  511. if (applicationName.GetLength()>100)
  512. return false;
  513. (*priorRowPatchTime)=0;
  514. (*mostRecentRowPatchTime)=0;
  515. const char *outTemp[2];
  516. int outLengths[2];
  517. int formats[2];
  518. if (applicationName.GetLength()==0)
  519. {
  520. strcpy(query,
  521. "SELECT tbl1.applicationName, tbl4.* FROM "
  522. "(SELECT applicationID, applicationName FROM Applications) as tbl1, "
  523. "(SELECT tbl2.applicationId, tbl2.fileId, tbl2.fileName, tbl2.fileLength, tbl2.contentHash, tbl2.createFile, tbl2.changeSetId, tbl2.content, tbl3.patch, tbl3.patchAlgorithm, tbl3.contentHash as priorHash FROM "
  524. "(SELECT * From FileVersionHistory WHERE modificationDate=(select MAX(modificationDate) from FileVersionHistory) AND changeSetId!=0 ) as tbl2 "
  525. "LEFT OUTER JOIN "
  526. "(SELECT patch, patchAlgorithm, fileName, contentHash FROM FileVersionHistory WHERE "
  527. "(changeSetId = (SELECT changeSetId FROM FileVersionHistory WHERE modificationDate=(select MAX(modificationDate) from FileVersionHistory) LIMIT 1) - 1 )) as tbl3 "
  528. "ON tbl2.filename=tbl3.filename) as tbl4; "
  529. );
  530. result = PQexecParams(pgConn, query,0,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  531. if (IsResultSuccessful(result, true)==false)
  532. {
  533. PQclear(result);
  534. return false;
  535. }
  536. }
  537. else
  538. {
  539. strcpy(query,
  540. "SELECT tbl2.fileId, tbl2.fileName, tbl2.fileLength, tbl2.contentHash, tbl2.createFile, tbl2.changeSetId, tbl2.content, tbl3.patch, tbl3.patchAlgorithm, tbl3.contentHash as priorHash FROM "
  541. "(SELECT * From FileVersionHistory WHERE changeSetId=(SELECT MAX(changeSetId) FROM Applications WHERE applicationName=$1::text)-1 AND changeSetId!=0 "
  542. ") as tbl2 "
  543. "LEFT OUTER JOIN "
  544. "(SELECT fileName, patch, patchAlgorithm, contentHash From FileVersionHistory WHERE changeSetId=(SELECT MAX(changeSetId) FROM Applications WHERE applicationName=$2::text)-2 "
  545. ") as tbl3 "
  546. "ON tbl2.filename=tbl3.filename;"
  547. );
  548. outTemp[0]=applicationName.C_String();
  549. outLengths[0]=(int) applicationName.GetLength();
  550. formats[0]=PQEXECPARAM_FORMAT_BINARY;
  551. outTemp[1]=applicationName.C_String();
  552. outLengths[1]=(int) applicationName.GetLength();
  553. formats[1]=PQEXECPARAM_FORMAT_BINARY;
  554. result = PQexecParams(pgConn, query,2,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  555. if (IsResultSuccessful(result, true)==false)
  556. {
  557. PQclear(result);
  558. return false;
  559. }
  560. }
  561. int numRows;
  562. numRows = PQntuples(result);
  563. if (numRows==0)
  564. {
  565. // Nothing was ever patched. However, read mostRecentRowPatchTime if possible
  566. if (applicationName.GetLength()==0)
  567. {
  568. // Lookup application if unspecified
  569. PGresult *result3;
  570. strcpy(query,
  571. "SELECT applicationName FROM Applications as tbl1, "
  572. "(SELECT applicationId From FileVersionHistory WHERE modificationDate=(select MAX(modificationDate) from FileVersionHistory LIMIT 1) LIMIT 1) as tbl2 "
  573. "WHERE tbl1.applicationID=tbl2.applicationID;"
  574. );
  575. result3 = PQexecParams(pgConn, query,0,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  576. if (IsResultSuccessful(result3, true)==false)
  577. {
  578. PQclear(result);
  579. return false;
  580. }
  581. if (PQntuples(result3)==0)
  582. {
  583. // No applications at all
  584. PQclear(result);
  585. PQclear(result3);
  586. return false;
  587. }
  588. int applicationNameColumnIndex = PQfnumber(result, "applicationName");
  589. applicationName = PQgetvalue(result3, 0, applicationNameColumnIndex);
  590. PQclear(result3);
  591. }
  592. }
  593. else if (applicationName.GetLength()==0)
  594. {
  595. int applicationNameColumnIndex = PQfnumber(result, "applicationName");
  596. applicationName = PQgetvalue(result, 0, applicationNameColumnIndex);
  597. }
  598. RakNet::RakString escapedApplicationName2 = GetEscapedString(applicationName);
  599. PGresult *result2;
  600. char *ts;
  601. int numRows2;
  602. // For the given application, get the highest file date
  603. sprintf(query,
  604. "SELECT modificationDate from FileVersionHistory WHERE changeSetId=(SELECT changeSetId FROM Applications WHERE applicationName='%s' AND changeSetId!=0 LIMIT 1)-1 LIMIT 1;"
  605. , escapedApplicationName2.C_String());
  606. if (ExecuteBlockingCommand(query, &result2, false)==false)
  607. {
  608. sprintf(lastError,"ERROR: Query is bad in file %s at line %i in function GetMostRecentChangelistWithPatches\n",_FILE_AND_LINE_);
  609. PQclear(result);
  610. PQclear(result2);
  611. return false;
  612. }
  613. numRows2 = PQntuples(result2);
  614. if (numRows2==0)
  615. {
  616. // No application
  617. PQclear(result);
  618. PQclear(result2);
  619. return false;
  620. }
  621. ts=PQgetvalue(result2, 0, 0);
  622. *mostRecentRowPatchTime=atof(ts);
  623. PQclear(result2);
  624. if (numRows==0)
  625. {
  626. // No patches to serve
  627. *priorRowPatchTime=0;
  628. PQclear(result);
  629. return true;
  630. }
  631. // In SQL, SELECT (EXTRACT(EPOCH FROM now())) is equivalent to time() function
  632. sprintf(query,
  633. "SELECT modificationDate from FileVersionHistory WHERE changeSetId=(SELECT changeSetId FROM Applications WHERE applicationName='%s' AND changeSetId!=0 LIMIT 1)-2 LIMIT 1;"
  634. , escapedApplicationName2.C_String());
  635. if (ExecuteBlockingCommand(query, &result2, false)==false)
  636. {
  637. sprintf(lastError,"ERROR: Query is bad in file %s at line %i in function GetMostRecentChangelistWithPatches\n",_FILE_AND_LINE_);
  638. PQclear(result);
  639. PQclear(result2);
  640. return false;
  641. }
  642. numRows2 = PQntuples(result2);
  643. if (numRows2==0)
  644. {
  645. sprintf(lastError,"ERROR: Query is bad in file %s at line %i in function GetMostRecentChangelistWithPatches\n",_FILE_AND_LINE_);
  646. PQclear(result);
  647. PQclear(result2);
  648. return false;
  649. }
  650. ts=PQgetvalue(result2, 0, 0);
  651. *priorRowPatchTime=atof(ts);
  652. PQclear(result2);
  653. int fileIdColumnIndex = PQfnumber(result, "fileId");
  654. int filenameColumnIndex = PQfnumber(result, "filename");
  655. int fileLengthColumnIndex = PQfnumber(result, "fileLength");
  656. int contentColumnIndex = PQfnumber(result, "content");
  657. int contentHashColumnIndex = PQfnumber(result, "contentHash");
  658. int patchColumnIndex = PQfnumber(result, "patch");
  659. int patchAlgorithmColumnIndex = PQfnumber(result, "patchAlgorithm");
  660. int priorHashColumnIndex = PQfnumber(result, "priorHash");
  661. int createFileColumnIndex = PQfnumber(result, "createFile");
  662. char *createFileResult;
  663. char *hardDriveFilename;
  664. char *hardDriveHash;
  665. char *fileData;
  666. char *patch;
  667. int rowIndex;
  668. int patchLength;
  669. char *contentHash;
  670. // AutopatcherPostgreRepository::GetPatches fills patchList with
  671. // either PC_WRITE_FILE and filename (but no data) when the user has no hash
  672. // PC_WRITE_FILE with filename (but no data) when the user has a hash that does not match any patch
  673. // PC_HASH_1_WITH_PATCH with the filename, (HASH,patch) if the patch was found
  674. for (rowIndex=0; rowIndex < numRows; rowIndex++)
  675. {
  676. createFileResult = PQgetvalue(result, rowIndex, createFileColumnIndex);
  677. hardDriveFilename = PQgetvalue(result, rowIndex, filenameColumnIndex);
  678. if (createFileResult[0]==1)
  679. {
  680. hardDriveHash = PQgetvalue(result, rowIndex, contentHashColumnIndex);
  681. int fileId = ntohl(*((int*)PQgetvalue(result, rowIndex, fileIdColumnIndex)));
  682. int fileLength = ntohl(*((int*)PQgetvalue(result, rowIndex, fileLengthColumnIndex)));
  683. contentHash = PQgetvalue(result, rowIndex, contentHashColumnIndex);
  684. patchLength=PQgetlength(result, rowIndex, patchColumnIndex);
  685. if (patchLength==0)
  686. {
  687. // New file that never before existed
  688. // OK
  689. addedOrModifiedFileHashes->AddFile(hardDriveFilename,hardDriveFilename, contentHash, HASH_LENGTH, fileLength, FileListNodeContext(PC_WRITE_FILE,fileId,0,0),false,false);
  690. fileData = PQgetvalue(result, rowIndex, contentColumnIndex);
  691. addedFiles->AddFile(hardDriveFilename,hardDriveFilename, fileData, fileLength, fileLength, FileListNodeContext(PC_WRITE_FILE,fileId,0,0),false,false);
  692. }
  693. else
  694. {
  695. // Patch to next version
  696. patch = PQgetvalue(result, rowIndex, patchColumnIndex);
  697. // int patchAlgorithm = ntohl(*((int*)PQgetvalue(result, rowIndex, patchAlgorithmColumnIndex)));
  698. const char *tv = PQgetvalue(result, rowIndex, patchAlgorithmColumnIndex);
  699. int patchAlgorithm;
  700. if (tv)
  701. patchAlgorithm = ntohl(*((int*)tv));
  702. else
  703. patchAlgorithm = 0;
  704. char *temp = (char *) rakMalloc_Ex(patchLength + HASH_LENGTH*2, _FILE_AND_LINE_ );
  705. RakAssert(temp);
  706. char *priorHash = PQgetvalue(result, rowIndex, priorHashColumnIndex);
  707. memcpy(temp, priorHash, HASH_LENGTH);
  708. memcpy(temp+HASH_LENGTH, contentHash, HASH_LENGTH);
  709. memcpy(temp+HASH_LENGTH*2, patch, patchLength);
  710. // OK
  711. addedOrModifiedFileHashes->AddFile(hardDriveFilename,hardDriveFilename, contentHash, HASH_LENGTH, fileLength, FileListNodeContext(PC_WRITE_FILE,fileId,0,0),false,false);
  712. patchedFiles->AddFile(hardDriveFilename,hardDriveFilename, temp, HASH_LENGTH*2+patchLength, patchLength, FileListNodeContext(PC_HASH_2_WITH_PATCH,fileId,patchAlgorithm,0), false, true );
  713. // fileData = PQgetvalue(result, rowIndex, contentColumnIndex);
  714. // updatedFiles->AddFile(hardDriveFilename,hardDriveFilename, fileData, fileLength, fileLength, FileListNodeContext(PC_WRITE_FILE,fileId),false,false);
  715. }
  716. }
  717. else
  718. {
  719. // Deleted file
  720. deletedFiles->AddFile(hardDriveFilename,hardDriveFilename,0,0,0,FileListNodeContext(0,0,0,0), false);
  721. }
  722. }
  723. PQclear(result);
  724. return true;
  725. }
  726. bool AutopatcherPostgreRepository2::GetMostRecentChangelistWithPatches(RakNet::RakString &applicationName, FileList *patchedFiles, FileList *addedFiles, FileList *addedOrModifiedFileHashes, FileList *deletedFiles, double *priorRowPatchTime, double *mostRecentRowPatchTime)
  727. {
  728. PGresult *result;
  729. char query[1024];
  730. if (applicationName.GetLength()>100)
  731. return false;
  732. (*priorRowPatchTime)=0;
  733. (*mostRecentRowPatchTime)=0;
  734. const char *outTemp[2];
  735. int outLengths[2];
  736. int formats[2];
  737. if (applicationName.GetLength()==0)
  738. {
  739. strcpy(query,
  740. "SELECT tbl1.applicationName, tbl4.* FROM "
  741. "(SELECT applicationID, applicationName FROM Applications) as tbl1, "
  742. "(SELECT tbl2.applicationId, tbl2.fileId, tbl2.fileName, tbl2.fileLength, tbl2.contentHash, tbl2.createFile, tbl2.changeSetId, tbl2.pathToContent, tbl3.patch, tbl3.patchAlgorithm, tbl3.contentHash as priorHash FROM "
  743. "(SELECT * From FileVersionHistory WHERE modificationDate=(select MAX(modificationDate) from FileVersionHistory) AND changeSetId!=0 ) as tbl2 "
  744. "LEFT OUTER JOIN "
  745. "(SELECT patch, patchAlgorithm, fileName, contentHash FROM FileVersionHistory WHERE "
  746. "(changeSetId = (SELECT changeSetId FROM FileVersionHistory WHERE modificationDate=(select MAX(modificationDate) from FileVersionHistory) LIMIT 1) - 1 )) as tbl3 "
  747. "ON tbl2.filename=tbl3.filename) as tbl4; "
  748. );
  749. result = PQexecParams(pgConn, query,0,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  750. if (IsResultSuccessful(result, true)==false)
  751. {
  752. PQclear(result);
  753. return false;
  754. }
  755. }
  756. else
  757. {
  758. strcpy(query,
  759. "SELECT tbl2.fileId, tbl2.fileName, tbl2.fileLength, tbl2.contentHash, tbl2.createFile, tbl2.changeSetId, tbl2.pathToContent, tbl3.patch, tbl3.patchAlgorithm, tbl3.contentHash as priorHash FROM "
  760. "(SELECT * From FileVersionHistory WHERE changeSetId=(SELECT MAX(changeSetId) FROM Applications WHERE applicationName=$1::text)-1 AND changeSetId!=0 "
  761. ") as tbl2 "
  762. "LEFT OUTER JOIN "
  763. "(SELECT fileName, patch, patchAlgorithm, contentHash From FileVersionHistory WHERE changeSetId=(SELECT MAX(changeSetId) FROM Applications WHERE applicationName=$2::text)-2 "
  764. ") as tbl3 "
  765. "ON tbl2.filename=tbl3.filename;"
  766. );
  767. outTemp[0]=applicationName.C_String();
  768. outLengths[0]=(int) applicationName.GetLength();
  769. formats[0]=PQEXECPARAM_FORMAT_BINARY;
  770. outTemp[1]=applicationName.C_String();
  771. outLengths[1]=(int) applicationName.GetLength();
  772. formats[1]=PQEXECPARAM_FORMAT_BINARY;
  773. result = PQexecParams(pgConn, query,2,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  774. if (IsResultSuccessful(result, true)==false)
  775. {
  776. PQclear(result);
  777. return false;
  778. }
  779. }
  780. int numRows;
  781. numRows = PQntuples(result);
  782. if (numRows==0)
  783. {
  784. // Nothing was ever patched. However, read mostRecentRowPatchTime if possible
  785. if (applicationName.GetLength()==0)
  786. {
  787. // Lookup application if unspecified
  788. PGresult *result3;
  789. strcpy(query,
  790. "SELECT applicationName FROM Applications as tbl1, "
  791. "(SELECT applicationId From FileVersionHistory WHERE modificationDate=(select MAX(modificationDate) from FileVersionHistory LIMIT 1) LIMIT 1) as tbl2 "
  792. "WHERE tbl1.applicationID=tbl2.applicationID;"
  793. );
  794. result3 = PQexecParams(pgConn, query,0,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  795. if (IsResultSuccessful(result3, true)==false)
  796. {
  797. PQclear(result);
  798. return false;
  799. }
  800. if (PQntuples(result3)==0)
  801. {
  802. // No applications at all
  803. PQclear(result);
  804. PQclear(result3);
  805. return false;
  806. }
  807. int applicationNameColumnIndex = PQfnumber(result, "applicationName");
  808. applicationName = PQgetvalue(result3, 0, applicationNameColumnIndex);
  809. PQclear(result3);
  810. }
  811. }
  812. else if (applicationName.GetLength()==0)
  813. {
  814. int applicationNameColumnIndex = PQfnumber(result, "applicationName");
  815. applicationName = PQgetvalue(result, 0, applicationNameColumnIndex);
  816. }
  817. RakNet::RakString escapedApplicationName2 = GetEscapedString(applicationName);
  818. PGresult *result2;
  819. char *ts;
  820. int numRows2;
  821. // For the given application, get the highest file date
  822. sprintf(query,
  823. "SELECT modificationDate from FileVersionHistory WHERE changeSetId=(SELECT changeSetId FROM Applications WHERE applicationName='%s' AND changeSetId!=0 LIMIT 1)-1 LIMIT 1;"
  824. , escapedApplicationName2.C_String());
  825. if (ExecuteBlockingCommand(query, &result2, false)==false)
  826. {
  827. sprintf(lastError,"ERROR: Query is bad in file %s at line %i in function GetMostRecentChangelistWithPatches\n",_FILE_AND_LINE_);
  828. PQclear(result);
  829. PQclear(result2);
  830. return false;
  831. }
  832. numRows2 = PQntuples(result2);
  833. if (numRows2==0)
  834. {
  835. // No application
  836. PQclear(result);
  837. PQclear(result2);
  838. return false;
  839. }
  840. ts=PQgetvalue(result2, 0, 0);
  841. *mostRecentRowPatchTime=atof(ts);
  842. PQclear(result2);
  843. if (numRows==0)
  844. {
  845. // No patches to serve
  846. *priorRowPatchTime=0;
  847. PQclear(result);
  848. return true;
  849. }
  850. // In SQL, SELECT (EXTRACT(EPOCH FROM now())) is equivalent to time() function
  851. sprintf(query,
  852. "SELECT modificationDate from FileVersionHistory WHERE changeSetId=(SELECT changeSetId FROM Applications WHERE applicationName='%s' AND changeSetId!=0 LIMIT 1)-2 LIMIT 1;"
  853. , escapedApplicationName2.C_String());
  854. if (ExecuteBlockingCommand(query, &result2, false)==false)
  855. {
  856. sprintf(lastError,"ERROR: Query is bad in file %s at line %i in function GetMostRecentChangelistWithPatches\n",_FILE_AND_LINE_);
  857. PQclear(result);
  858. PQclear(result2);
  859. return false;
  860. }
  861. numRows2 = PQntuples(result2);
  862. if (numRows2==0)
  863. {
  864. sprintf(lastError,"ERROR: Query is bad in file %s at line %i in function GetMostRecentChangelistWithPatches\n",_FILE_AND_LINE_);
  865. PQclear(result);
  866. PQclear(result2);
  867. return false;
  868. }
  869. ts=PQgetvalue(result2, 0, 0);
  870. *priorRowPatchTime=atof(ts);
  871. PQclear(result2);
  872. int fileIdColumnIndex = PQfnumber(result, "fileId");
  873. int filenameColumnIndex = PQfnumber(result, "filename");
  874. int fileLengthColumnIndex = PQfnumber(result, "fileLength");
  875. int pathToContentColumnIndex = PQfnumber(result, "pathToContent");
  876. int contentHashColumnIndex = PQfnumber(result, "contentHash");
  877. int patchColumnIndex = PQfnumber(result, "patch");
  878. int patchAlgorithmColumnIndex = PQfnumber(result, "patchAlgorithm");
  879. int priorHashColumnIndex = PQfnumber(result, "priorHash");
  880. int createFileColumnIndex = PQfnumber(result, "createFile");
  881. char *createFileResult;
  882. char *hardDriveFilename;
  883. char *hardDriveHash;
  884. //char *fileData;
  885. char *patch;
  886. int rowIndex;
  887. int patchLength;
  888. char *contentHash;
  889. // AutopatcherPostgreRepository::GetPatches fills patchList with
  890. // either PC_WRITE_FILE and filename (but no data) when the user has no hash
  891. // PC_WRITE_FILE with filename (but no data) when the user has a hash that does not match any patch
  892. // PC_HASH_1_WITH_PATCH with the filename, (HASH,patch) if the patch was found
  893. for (rowIndex=0; rowIndex < numRows; rowIndex++)
  894. {
  895. createFileResult = PQgetvalue(result, rowIndex, createFileColumnIndex);
  896. hardDriveFilename = PQgetvalue(result, rowIndex, filenameColumnIndex);
  897. if (createFileResult[0]==1)
  898. {
  899. hardDriveHash = PQgetvalue(result, rowIndex, contentHashColumnIndex);
  900. int fileId = ntohl(*((int*)PQgetvalue(result, rowIndex, fileIdColumnIndex)));
  901. int fileLength = ntohl(*((int*)PQgetvalue(result, rowIndex, fileLengthColumnIndex)));
  902. contentHash = PQgetvalue(result, rowIndex, contentHashColumnIndex);
  903. patchLength=PQgetlength(result, rowIndex, patchColumnIndex);
  904. if (patchLength==0)
  905. {
  906. // New file that never before existed
  907. // fileData is given to AddFile
  908. char *pathToContent = PQgetvalue(result, rowIndex, pathToContentColumnIndex);
  909. FILE *fp = fopen(pathToContent, "rb");
  910. if (fp==0)
  911. {
  912. sprintf(lastError,"ERROR: Cannot open file %s in file %s at line %i in function GetMostRecentChangelistWithPatches\n",pathToContent, _FILE_AND_LINE_);
  913. PQclear(result);
  914. return false;
  915. }
  916. char *fileData = (char*) rakMalloc_Ex(fileLength, _FILE_AND_LINE_);
  917. RakAssert(fileData);
  918. fread(fileData, fileLength, 1, fp);
  919. fclose(fp);
  920. // OK
  921. addedOrModifiedFileHashes->AddFile(hardDriveFilename,hardDriveFilename, contentHash, HASH_LENGTH, fileLength, FileListNodeContext(PC_WRITE_FILE,fileId,0,0),false,false);
  922. // fileData = PQgetvalue(result, rowIndex, contentColumnIndex);
  923. // const char *pathToContent = PQgetvalue(result, rowIndex, pathToContentColumnIndex);
  924. // Last parameter is take the fileData pointer
  925. addedFiles->AddFile(hardDriveFilename,hardDriveFilename, fileData, fileLength, fileLength, FileListNodeContext(PC_WRITE_FILE,fileId,0,0),false,true);
  926. }
  927. else
  928. {
  929. // Patch to next version
  930. patch = PQgetvalue(result, rowIndex, patchColumnIndex);
  931. const char *tv = PQgetvalue(result, rowIndex, patchAlgorithmColumnIndex);
  932. int patchAlgorithm;
  933. if (tv)
  934. patchAlgorithm = ntohl(*((int*)tv));
  935. else
  936. patchAlgorithm = 0;
  937. char *temp = (char *) rakMalloc_Ex(patchLength + HASH_LENGTH*2, _FILE_AND_LINE_ );
  938. RakAssert(temp);
  939. char *priorHash = PQgetvalue(result, rowIndex, priorHashColumnIndex);
  940. memcpy(temp, priorHash, HASH_LENGTH);
  941. memcpy(temp+HASH_LENGTH, contentHash, HASH_LENGTH);
  942. memcpy(temp+HASH_LENGTH*2, patch, patchLength);
  943. // OK
  944. addedOrModifiedFileHashes->AddFile(hardDriveFilename,hardDriveFilename, contentHash, HASH_LENGTH, fileLength, FileListNodeContext(PC_WRITE_FILE,fileId,0,0),false,false);
  945. patchedFiles->AddFile(hardDriveFilename,hardDriveFilename, temp, HASH_LENGTH*2+patchLength, patchLength, FileListNodeContext(PC_HASH_2_WITH_PATCH,fileId,patchAlgorithm,0), false, true );
  946. // fileData = PQgetvalue(result, rowIndex, contentColumnIndex);
  947. // updatedFiles->AddFile(hardDriveFilename,hardDriveFilename, fileData, fileLength, fileLength, FileListNodeContext(PC_WRITE_FILE,fileId),false,false);
  948. }
  949. }
  950. else
  951. {
  952. // Deleted file
  953. deletedFiles->AddFile(hardDriveFilename,hardDriveFilename,0,0,0,FileListNodeContext(0,0,0,0), false);
  954. }
  955. }
  956. PQclear(result);
  957. return true;
  958. }
  959. bool AutopatcherPostgreRepository::UpdateApplicationFiles(const char *applicationName, const char *applicationDirectory, const char *userName, FileListProgress *cb)
  960. {
  961. FileList filesOnHarddrive;
  962. filesOnHarddrive.AddCallback(cb);
  963. filesOnHarddrive.AddFilesFromDirectory(applicationDirectory,"", true, false, true, FileListNodeContext(0,0,0,0));
  964. if (filesOnHarddrive.fileList.Size()==0)
  965. {
  966. sprintf(lastError,"ERROR: Can't find files at %s in UpdateApplicationFiles\n",applicationDirectory);
  967. return false;
  968. }
  969. int numRows;
  970. PGresult *result;
  971. char query[512];
  972. if (strlen(applicationName)>100)
  973. return false;
  974. if (strlen(userName)>100)
  975. return false;
  976. RakNet::RakString escapedApplicationName = GetEscapedString(applicationName);
  977. sprintf(query, "SELECT applicationID FROM applications WHERE applicationName='%s';", escapedApplicationName.C_String());
  978. //sqlCommandMutex.Lock();
  979. if (ExecuteBlockingCommand(query, &result, false)==false)
  980. {
  981. //sqlCommandMutex.Unlock();
  982. PQclear(result);
  983. return false;
  984. }
  985. //sqlCommandMutex.Unlock();
  986. numRows = PQntuples(result);
  987. if (numRows==0)
  988. {
  989. sprintf(lastError,"ERROR: %s not found in UpdateApplicationFiles\n",applicationName);
  990. return false;
  991. }
  992. char *res;
  993. res = PQgetvalue(result,0,0);
  994. int applicationID;
  995. applicationID=atoi(res);
  996. PQclear(result);
  997. // If ExecuteBlockingCommand fails then it does a rollback
  998. //sqlCommandMutex.Lock();
  999. if (ExecuteBlockingCommand("BEGIN;", &result, false)==false)
  1000. {
  1001. //sqlCommandMutex.Unlock();
  1002. PQclear(result);
  1003. return false;
  1004. }
  1005. //sqlCommandMutex.Unlock();
  1006. PQclear(result);
  1007. sprintf(query, "UPDATE applications SET changeSetId = changeSetId + 1 where applicationID=%i; SELECT changeSetId FROM applications WHERE applicationID=%i;", applicationID, applicationID);
  1008. //sqlCommandMutex.Lock();
  1009. if (ExecuteBlockingCommand(query, &result, true)==false)
  1010. {
  1011. //sqlCommandMutex.Unlock();
  1012. PQclear(result);
  1013. return false;
  1014. }
  1015. //sqlCommandMutex.Unlock();
  1016. numRows = PQntuples(result);
  1017. if (numRows==0)
  1018. {
  1019. sprintf(lastError,"ERROR: applicationID %i not found in UpdateApplicationFiles\n", applicationID);
  1020. Rollback();
  1021. return false;
  1022. }
  1023. res = PQgetvalue(result,0,0);
  1024. int changeSetId;
  1025. changeSetId=atoi(res);
  1026. PQclear(result);
  1027. // +1 was added in the update
  1028. changeSetId--;
  1029. // Gets all newest files
  1030. // TODO - This can be non-blocking
  1031. sprintf(query, "SELECT DISTINCT ON (filename) filename, contentHash, createFile FROM FileVersionHistory WHERE applicationID=%i ORDER BY filename, fileId DESC;", applicationID);
  1032. //sqlCommandMutex.Lock();
  1033. result = PQexecParams(pgConn, query,0,0,0,0,0,PQEXECPARAM_FORMAT_BINARY);
  1034. //sqlCommandMutex.Unlock();
  1035. if (IsResultSuccessful(result, true)==false)
  1036. {
  1037. PQclear(result);
  1038. return false;
  1039. }
  1040. int filenameColumnIndex = PQfnumber(result, "filename");
  1041. int contentHashColumnIndex = PQfnumber(result, "contentHash");
  1042. int createFileColumnIndex = PQfnumber(result, "createFile");
  1043. //char *PQgetvalue(result,int row_number,int column_number);
  1044. //int PQgetlength(result, int row_number, int column_number);
  1045. unsigned fileListIndex;
  1046. int rowIndex;
  1047. RakNet::RakString hardDriveFilename;
  1048. char *hardDriveHash;
  1049. RakNet::RakString queryFilename;
  1050. char *createFileResult;
  1051. char *hash;
  1052. bool addFile;
  1053. FileList newFiles;
  1054. numRows = PQntuples(result);
  1055. // Loop through files om filesOnHarddrive
  1056. // If the file in filesOnHarddrive does not exist in the query result, or if it does but the hash is different or non-existent, add this file to the create list
  1057. for (fileListIndex=0; fileListIndex < filesOnHarddrive.fileList.Size(); fileListIndex++)
  1058. {
  1059. if (fileListIndex%10==0)
  1060. printf("Hashing files %i/%i\n", fileListIndex+1, filesOnHarddrive.fileList.Size());
  1061. addFile=true;
  1062. hardDriveFilename=filesOnHarddrive.fileList[fileListIndex].filename;
  1063. hardDriveHash=filesOnHarddrive.fileList[fileListIndex].data;
  1064. for (rowIndex=0; rowIndex < numRows; rowIndex++ )
  1065. {
  1066. queryFilename = PQgetvalue(result, rowIndex, filenameColumnIndex);
  1067. if (_stricmp(hardDriveFilename, queryFilename)==0)
  1068. {
  1069. createFileResult = PQgetvalue(result, rowIndex, createFileColumnIndex);
  1070. hash = PQgetvalue(result, rowIndex, contentHashColumnIndex);
  1071. if (createFileResult[0]==1 && memcmp(hash, hardDriveHash, HASH_LENGTH)==0)
  1072. {
  1073. // File exists in database and is the same
  1074. addFile=false;
  1075. }
  1076. break;
  1077. }
  1078. }
  1079. // Unless set to false, file does not exist in query result or is different.
  1080. if (addFile==true)
  1081. {
  1082. newFiles.AddFile(hardDriveFilename,hardDriveFilename, filesOnHarddrive.fileList[fileListIndex].data, filesOnHarddrive.fileList[fileListIndex].dataLengthBytes, filesOnHarddrive.fileList[fileListIndex].fileLengthBytes, FileListNodeContext(0,0,0,0), false);
  1083. }
  1084. }
  1085. // Go through query results that are marked as create
  1086. // If a file that is currently on the database is not on the harddrive, add it to the delete list
  1087. FileList deletedFiles;
  1088. bool fileOnHarddrive;
  1089. for (rowIndex=0; rowIndex < numRows; rowIndex++ )
  1090. {
  1091. queryFilename = PQgetvalue(result, rowIndex, filenameColumnIndex);
  1092. createFileResult = PQgetvalue(result, rowIndex, createFileColumnIndex);
  1093. if (createFileResult[0]!=1)
  1094. continue; // If already false don't mark false again.
  1095. fileOnHarddrive=false;
  1096. for (fileListIndex=0; fileListIndex < filesOnHarddrive.fileList.Size(); fileListIndex++)
  1097. {
  1098. hardDriveFilename=filesOnHarddrive.fileList[fileListIndex].filename.C_String();
  1099. hardDriveHash=filesOnHarddrive.fileList[fileListIndex].data;
  1100. if (_stricmp(hardDriveFilename, queryFilename)==0)
  1101. {
  1102. fileOnHarddrive=true;
  1103. break;
  1104. }
  1105. }
  1106. if (fileOnHarddrive==false)
  1107. deletedFiles.AddFile(queryFilename,queryFilename,0,0,0,FileListNodeContext(0,0,0,0), false);
  1108. }
  1109. // Query memory and files on harddrive no longer needed. Free this memory since generating all the patches is memory intensive.
  1110. PQclear(result);
  1111. filesOnHarddrive.Clear();
  1112. const char *outTemp[3];
  1113. int outLengths[3];
  1114. int formats[3];
  1115. formats[0]=PQEXECPARAM_FORMAT_TEXT;
  1116. formats[1]=PQEXECPARAM_FORMAT_BINARY; // Always happens to be binary
  1117. formats[2]=PQEXECPARAM_FORMAT_BINARY; // Always happens to be binary
  1118. // For each file in the delete list add a row indicating file deletion
  1119. for (fileListIndex=0; fileListIndex < deletedFiles.fileList.Size(); fileListIndex++)
  1120. {
  1121. if (fileListIndex%10==0)
  1122. printf("Tagging deleted files %i/%i\n", fileListIndex+1, deletedFiles.fileList.Size());
  1123. // BUGGED
  1124. sprintf(query, "INSERT INTO FileVersionHistory(applicationID, filename, createFile, changeSetID, userName) VALUES (%i, $1::text,FALSE,%i,'%s');", applicationID, changeSetId, GetEscapedString(userName).C_String());
  1125. outTemp[0]=deletedFiles.fileList[fileListIndex].filename.C_String();
  1126. outLengths[0]=(int)deletedFiles.fileList[fileListIndex].filename.GetLength();
  1127. formats[0]=PQEXECPARAM_FORMAT_TEXT;
  1128. //sqlCommandMutex.Lock();
  1129. result = PQexecParams(pgConn, query,1,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  1130. //sqlCommandMutex.Unlock();
  1131. if (IsResultSuccessful(result, true)==false)
  1132. {
  1133. deletedFiles.Clear();
  1134. newFiles.Clear();
  1135. PQclear(result);
  1136. return false;
  1137. }
  1138. PQclear(result);
  1139. }
  1140. // Clear the delete list as it is no longer needed.
  1141. deletedFiles.Clear();
  1142. int contentColumnIndex;
  1143. PGresult *uploadResult;
  1144. PGresult *fileRows;
  1145. char *content;
  1146. char *fileID;
  1147. int contentLength;
  1148. int fileIDLength;
  1149. int fileIDColumnIndex;
  1150. char *hardDriveData;
  1151. unsigned hardDriveDataLength;
  1152. char *patch;
  1153. unsigned patchLength;
  1154. // For each file in the create list
  1155. for (fileListIndex=0; fileListIndex < newFiles.fileList.Size(); fileListIndex++)
  1156. {
  1157. if (fileListIndex%10==0)
  1158. printf("Adding file %i/%i\n", fileListIndex+1, newFiles.fileList.Size());
  1159. hardDriveFilename=newFiles.fileList[fileListIndex].filename;
  1160. // hardDriveData=newFiles.fileList[fileListIndex].data+HASH_LENGTH;
  1161. hardDriveHash=newFiles.fileList[fileListIndex].data;
  1162. hardDriveDataLength=newFiles.fileList[fileListIndex].fileLengthBytes;
  1163. char path[MAX_PATH];
  1164. strcpy(path, applicationDirectory);
  1165. strcat(path, "/");
  1166. strcat(path, newFiles.fileList[fileListIndex].fullPathToFile);
  1167. FILE *fp = fopen(path, "rb");
  1168. if (fp==0)
  1169. {
  1170. newFiles.Clear();
  1171. PQclear(fileRows);
  1172. RakAssert(0);
  1173. return false;
  1174. }
  1175. hardDriveData =(char*) rakMalloc_Ex( hardDriveDataLength, _FILE_AND_LINE_ );
  1176. RakAssert(hardDriveData);
  1177. fread(hardDriveData,1,hardDriveDataLength,fp);
  1178. fclose(fp);
  1179. sprintf( query, "SELECT fileID from FileVersionHistory WHERE applicationID=%i AND filename=$1::text AND createFile=TRUE;", applicationID );
  1180. outTemp[0]=hardDriveFilename;
  1181. outLengths[0]=(int)strlen(hardDriveFilename);
  1182. formats[0]=PQEXECPARAM_FORMAT_TEXT;
  1183. //sqlCommandMutex.Lock();
  1184. fileRows = PQexecParams(pgConn, query,1,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_TEXT);
  1185. //sqlCommandMutex.Unlock();
  1186. if (IsResultSuccessful(fileRows, true)==false)
  1187. {
  1188. rakFree_Ex(hardDriveData, _FILE_AND_LINE_);
  1189. newFiles.Clear();
  1190. PQclear(fileRows);
  1191. RakAssert(0);
  1192. return false;
  1193. }
  1194. fileIDColumnIndex = PQfnumber( fileRows, "fileID" );
  1195. numRows = PQntuples(fileRows);
  1196. outTemp[0]=hardDriveFilename;
  1197. outLengths[0]=(int)strlen(hardDriveFilename);
  1198. // Create new patches for every create version
  1199. for (rowIndex=0; rowIndex < numRows; rowIndex++ )
  1200. {
  1201. fileIDLength=PQgetlength(fileRows, rowIndex, fileIDColumnIndex);
  1202. fileID=PQgetvalue(fileRows, rowIndex, fileIDColumnIndex);
  1203. formats[0]=PQEXECPARAM_FORMAT_TEXT;
  1204. outTemp[0]=fileID;
  1205. outLengths[0]=fileIDLength;
  1206. // The last query handled all the relevant comparisons
  1207. sprintf(query, "SELECT content from FileVersionHistory WHERE fileID=$1::int;" );
  1208. //sqlCommandMutex.Lock();
  1209. result = PQexecParams(pgConn, query,1,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  1210. //sqlCommandMutex.Unlock();
  1211. if (IsResultSuccessful(result, true)==false)
  1212. {
  1213. rakFree_Ex(hardDriveData, _FILE_AND_LINE_);
  1214. Rollback();
  1215. newFiles.Clear();
  1216. PQclear(result);
  1217. return false;
  1218. }
  1219. int numContent = PQntuples(result);
  1220. if( numContent > 1 || numContent == 0 )
  1221. {
  1222. rakFree_Ex(hardDriveData, _FILE_AND_LINE_);
  1223. Rollback();
  1224. newFiles.Clear();
  1225. PQclear(result);
  1226. PQclear(fileRows);
  1227. RakAssert(0);
  1228. return false;
  1229. }
  1230. formats[0] = PQEXECPARAM_FORMAT_TEXT;
  1231. contentColumnIndex = PQfnumber(result, "content");
  1232. contentLength=PQgetlength(result, 0, contentColumnIndex);
  1233. content=PQgetvalue(result, 0, contentColumnIndex);
  1234. if (CreatePatch(content, contentLength, hardDriveData, hardDriveDataLength, &patch, &patchLength)==false)
  1235. {
  1236. rakFree_Ex(hardDriveData, _FILE_AND_LINE_);
  1237. strcpy(lastError,"CreatePatch failed.\n");
  1238. Rollback();
  1239. newFiles.Clear();
  1240. PQclear(result);
  1241. PQclear(fileRows);
  1242. return false;
  1243. }
  1244. // outTemp[0]=fileID;
  1245. // outLengths[0]=fileIDLength;
  1246. // outTemp[1]=patch;
  1247. // outLengths[1]=patchLength;
  1248. outTemp[0]=patch;
  1249. outLengths[0]=patchLength;
  1250. formats[0]=PQEXECPARAM_FORMAT_BINARY;
  1251. //sqlCommandMutex.Lock();
  1252. char buff[256];
  1253. sprintf(buff, "UPDATE FileVersionHistory SET patch=$1::bytea, patchAlgorithm=0 where fileID=%s;", fileID);
  1254. uploadResult = PQexecParams(pgConn, buff, 1,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  1255. // Done with this patch data
  1256. delete [] patch;
  1257. //sqlCommandMutex.Unlock();
  1258. if (IsResultSuccessful(uploadResult, true)==false)
  1259. {
  1260. rakFree_Ex(hardDriveData, _FILE_AND_LINE_);
  1261. Rollback();
  1262. newFiles.Clear();
  1263. PQclear(result);
  1264. return false;
  1265. }
  1266. PQclear(result);
  1267. PQclear(uploadResult);
  1268. }
  1269. PQclear(fileRows);
  1270. // Add totally new files
  1271. sprintf(query, "INSERT INTO FileVersionHistory (applicationID, filename, fileLength, content, contentHash, createFile, changeSetID, userName) "
  1272. "VALUES ("
  1273. "%i,"
  1274. "$1::text,"
  1275. "%i,"
  1276. "$2::bytea,"
  1277. "$3::bytea,"
  1278. "TRUE,"
  1279. "%i,"
  1280. "'%s'"
  1281. ");", applicationID, hardDriveDataLength, changeSetId, GetEscapedString(userName).C_String());
  1282. outTemp[0]=hardDriveFilename;
  1283. outTemp[1]=hardDriveData;
  1284. outTemp[2]=hardDriveHash;
  1285. outLengths[0]=(int)strlen(hardDriveFilename);
  1286. outLengths[1]=hardDriveDataLength;
  1287. outLengths[2]=HASH_LENGTH;
  1288. formats[0]=PQEXECPARAM_FORMAT_TEXT;
  1289. RakAssert(formats[1]==PQEXECPARAM_FORMAT_BINARY);
  1290. RakAssert(formats[2]==PQEXECPARAM_FORMAT_BINARY);
  1291. // Upload the new file
  1292. //sqlCommandMutex.Lock();
  1293. uploadResult = PQexecParams(pgConn, query,3,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  1294. rakFree_Ex(hardDriveData, _FILE_AND_LINE_);
  1295. //sqlCommandMutex.Unlock();
  1296. if( !uploadResult )
  1297. {
  1298. // Libpq had a problem inserting the file to the table. Most likely due to it running out of
  1299. // memory or a buffer being too small.
  1300. RakAssert( 0 );
  1301. newFiles.Clear();
  1302. PQclear(result);
  1303. return false;
  1304. }
  1305. if (IsResultSuccessful(uploadResult, true)==false)
  1306. {
  1307. newFiles.Clear();
  1308. PQclear(uploadResult);
  1309. PQclear(result);
  1310. return false;
  1311. }
  1312. // Clear the upload result
  1313. PQclear(uploadResult);
  1314. }
  1315. printf("DB COMMIT\n");
  1316. // If ExecuteBlockingCommand fails then it does a rollback
  1317. //sqlCommandMutex.Lock();
  1318. if (ExecuteBlockingCommand("COMMIT;", &result, true)==false)
  1319. {
  1320. //sqlCommandMutex.Unlock();
  1321. printf("COMMIT Failed!\n");
  1322. PQclear(result);
  1323. return false;
  1324. }
  1325. //sqlCommandMutex.Unlock();
  1326. PQclear(result);
  1327. printf("COMMIT Success\n");
  1328. return true;
  1329. }
  1330. bool AutopatcherPostgreRepository2::UpdateApplicationFiles(const char *applicationName, const char *applicationDirectory, const char *userName, FileListProgress *cb)
  1331. {
  1332. FileList filesOnHarddrive;
  1333. filesOnHarddrive.AddCallback(cb);
  1334. filesOnHarddrive.AddFilesFromDirectory(applicationDirectory,"", true, false, true, FileListNodeContext(0,0,0,0));
  1335. if (filesOnHarddrive.fileList.Size()==0)
  1336. {
  1337. sprintf(lastError,"ERROR: Can't find files at %s in UpdateApplicationFiles\n",applicationDirectory);
  1338. return false;
  1339. }
  1340. int numRows;
  1341. PGresult *result;
  1342. char query[512];
  1343. if (strlen(applicationName)>100)
  1344. return false;
  1345. if (strlen(userName)>100)
  1346. return false;
  1347. RakNet::RakString escapedApplicationName = GetEscapedString(applicationName);
  1348. sprintf(query, "SELECT applicationID FROM applications WHERE applicationName='%s';", escapedApplicationName.C_String());
  1349. //sqlCommandMutex.Lock();
  1350. if (ExecuteBlockingCommand(query, &result, false)==false)
  1351. {
  1352. //sqlCommandMutex.Unlock();
  1353. PQclear(result);
  1354. return false;
  1355. }
  1356. //sqlCommandMutex.Unlock();
  1357. numRows = PQntuples(result);
  1358. if (numRows==0)
  1359. {
  1360. sprintf(lastError,"ERROR: %s not found in UpdateApplicationFiles\n",applicationName);
  1361. return false;
  1362. }
  1363. char *res;
  1364. res = PQgetvalue(result,0,0);
  1365. int applicationID;
  1366. applicationID=atoi(res);
  1367. PQclear(result);
  1368. // If ExecuteBlockingCommand fails then it does a rollback
  1369. //sqlCommandMutex.Lock();
  1370. if (ExecuteBlockingCommand("BEGIN;", &result, false)==false)
  1371. {
  1372. //sqlCommandMutex.Unlock();
  1373. PQclear(result);
  1374. return false;
  1375. }
  1376. //sqlCommandMutex.Unlock();
  1377. PQclear(result);
  1378. sprintf(query, "UPDATE applications SET changeSetId = changeSetId + 1 where applicationID=%i; SELECT changeSetId FROM applications WHERE applicationID=%i;", applicationID, applicationID);
  1379. //sqlCommandMutex.Lock();
  1380. if (ExecuteBlockingCommand(query, &result, true)==false)
  1381. {
  1382. //sqlCommandMutex.Unlock();
  1383. PQclear(result);
  1384. return false;
  1385. }
  1386. //sqlCommandMutex.Unlock();
  1387. numRows = PQntuples(result);
  1388. if (numRows==0)
  1389. {
  1390. sprintf(lastError,"ERROR: applicationID %i not found in UpdateApplicationFiles\n", applicationID);
  1391. Rollback();
  1392. return false;
  1393. }
  1394. res = PQgetvalue(result,0,0);
  1395. int changeSetId;
  1396. changeSetId=atoi(res);
  1397. PQclear(result);
  1398. // +1 was added in the update
  1399. changeSetId--;
  1400. // Gets all newest files
  1401. // TODO - This can be non-blocking
  1402. sprintf(query, "SELECT DISTINCT ON (filename) filename, contentHash, createFile FROM FileVersionHistory WHERE applicationID=%i ORDER BY filename, fileId DESC;", applicationID);
  1403. //sqlCommandMutex.Lock();
  1404. result = PQexecParams(pgConn, query,0,0,0,0,0,PQEXECPARAM_FORMAT_BINARY);
  1405. //sqlCommandMutex.Unlock();
  1406. if (IsResultSuccessful(result, true)==false)
  1407. {
  1408. PQclear(result);
  1409. return false;
  1410. }
  1411. int filenameColumnIndex = PQfnumber(result, "filename");
  1412. int contentHashColumnIndex = PQfnumber(result, "contentHash");
  1413. int createFileColumnIndex = PQfnumber(result, "createFile");
  1414. //char *PQgetvalue(result,int row_number,int column_number);
  1415. //int PQgetlength(result, int row_number, int column_number);
  1416. unsigned fileListIndex;
  1417. int rowIndex;
  1418. RakNet::RakString hardDriveFilename;
  1419. char *hardDriveHash;
  1420. RakNet::RakString queryFilename;
  1421. char *createFileResult;
  1422. char *hash;
  1423. bool addFile;
  1424. FileList newFiles;
  1425. numRows = PQntuples(result);
  1426. // Loop through files om filesOnHarddrive
  1427. // If the file in filesOnHarddrive does not exist in the query result, or if it does but the hash is different or non-existent, add this file to the create list
  1428. for (fileListIndex=0; fileListIndex < filesOnHarddrive.fileList.Size(); fileListIndex++)
  1429. {
  1430. if (fileListIndex%10==0)
  1431. printf("Hashing files %i/%i\n", fileListIndex+1, filesOnHarddrive.fileList.Size());
  1432. addFile=true;
  1433. hardDriveFilename=filesOnHarddrive.fileList[fileListIndex].filename;
  1434. hardDriveHash=filesOnHarddrive.fileList[fileListIndex].data;
  1435. for (rowIndex=0; rowIndex < numRows; rowIndex++ )
  1436. {
  1437. queryFilename = PQgetvalue(result, rowIndex, filenameColumnIndex);
  1438. if (_stricmp(hardDriveFilename, queryFilename)==0)
  1439. {
  1440. createFileResult = PQgetvalue(result, rowIndex, createFileColumnIndex);
  1441. hash = PQgetvalue(result, rowIndex, contentHashColumnIndex);
  1442. if (createFileResult[0]==1 && memcmp(hash, hardDriveHash, HASH_LENGTH)==0)
  1443. {
  1444. // File exists in database and is the same
  1445. addFile=false;
  1446. }
  1447. break;
  1448. }
  1449. }
  1450. // Unless set to false, file does not exist in query result or is different.
  1451. if (addFile==true)
  1452. {
  1453. newFiles.AddFile(hardDriveFilename,hardDriveFilename, filesOnHarddrive.fileList[fileListIndex].data, filesOnHarddrive.fileList[fileListIndex].dataLengthBytes, filesOnHarddrive.fileList[fileListIndex].fileLengthBytes, FileListNodeContext(0,0,0,0), false);
  1454. }
  1455. }
  1456. // Go through query results that are marked as create
  1457. // If a file that is currently on the database is not on the harddrive, add it to the delete list
  1458. FileList deletedFiles;
  1459. bool fileOnHarddrive;
  1460. for (rowIndex=0; rowIndex < numRows; rowIndex++ )
  1461. {
  1462. queryFilename = PQgetvalue(result, rowIndex, filenameColumnIndex);
  1463. createFileResult = PQgetvalue(result, rowIndex, createFileColumnIndex);
  1464. if (createFileResult[0]!=1)
  1465. continue; // If already false don't mark false again.
  1466. fileOnHarddrive=false;
  1467. for (fileListIndex=0; fileListIndex < filesOnHarddrive.fileList.Size(); fileListIndex++)
  1468. {
  1469. hardDriveFilename=filesOnHarddrive.fileList[fileListIndex].filename.C_String();
  1470. hardDriveHash=filesOnHarddrive.fileList[fileListIndex].data;
  1471. if (_stricmp(hardDriveFilename, queryFilename)==0)
  1472. {
  1473. fileOnHarddrive=true;
  1474. break;
  1475. }
  1476. }
  1477. if (fileOnHarddrive==false)
  1478. deletedFiles.AddFile(queryFilename,queryFilename,0,0,0,FileListNodeContext(0,0,0,0), false);
  1479. }
  1480. // Query memory and files on harddrive no longer needed. Free this memory since generating all the patches is memory intensive.
  1481. PQclear(result);
  1482. filesOnHarddrive.Clear();
  1483. const char *outTemp[3];
  1484. int outLengths[3];
  1485. int formats[3];
  1486. formats[0]=PQEXECPARAM_FORMAT_TEXT;
  1487. formats[1]=PQEXECPARAM_FORMAT_BINARY; // Always happens to be binary
  1488. formats[2]=PQEXECPARAM_FORMAT_BINARY; // Always happens to be binary
  1489. // For each file in the delete list add a row indicating file deletion
  1490. for (fileListIndex=0; fileListIndex < deletedFiles.fileList.Size(); fileListIndex++)
  1491. {
  1492. if (fileListIndex%10==0)
  1493. printf("Tagging deleted files %i/%i\n", fileListIndex+1, deletedFiles.fileList.Size());
  1494. // BUGGED
  1495. sprintf(query, "INSERT INTO FileVersionHistory(applicationID, filename, createFile, changeSetID, userName) VALUES (%i, $1::text,FALSE,%i,'%s');", applicationID, changeSetId, GetEscapedString(userName).C_String());
  1496. outTemp[0]=deletedFiles.fileList[fileListIndex].filename.C_String();
  1497. outLengths[0]=(int)deletedFiles.fileList[fileListIndex].filename.GetLength();
  1498. formats[0]=PQEXECPARAM_FORMAT_TEXT;
  1499. //sqlCommandMutex.Lock();
  1500. result = PQexecParams(pgConn, query,1,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  1501. //sqlCommandMutex.Unlock();
  1502. if (IsResultSuccessful(result, true)==false)
  1503. {
  1504. deletedFiles.Clear();
  1505. newFiles.Clear();
  1506. PQclear(result);
  1507. return false;
  1508. }
  1509. PQclear(result);
  1510. }
  1511. // Clear the delete list as it is no longer needed.
  1512. deletedFiles.Clear();
  1513. int contentColumnIndex;
  1514. PGresult *uploadResult;
  1515. PGresult *fileRows;
  1516. char *fileID;
  1517. //int contentLength;
  1518. int fileIDLength;
  1519. int fileIDColumnIndex;
  1520. // char *newContent;
  1521. unsigned hardDriveDataLength;
  1522. char *patch;
  1523. unsigned patchLength;
  1524. // For each file in the create list
  1525. for (fileListIndex=0; fileListIndex < newFiles.fileList.Size(); fileListIndex++)
  1526. {
  1527. if (fileListIndex%10==0)
  1528. printf("Adding file %i/%i\n", fileListIndex+1, newFiles.fileList.Size());
  1529. hardDriveFilename=newFiles.fileList[fileListIndex].filename;
  1530. // hardDriveData=newFiles.fileList[fileListIndex].data+HASH_LENGTH;
  1531. hardDriveHash=newFiles.fileList[fileListIndex].data;
  1532. hardDriveDataLength=newFiles.fileList[fileListIndex].fileLengthBytes;
  1533. char pathToNewContent[MAX_PATH];
  1534. strcpy(pathToNewContent, applicationDirectory);
  1535. strcat(pathToNewContent, "/");
  1536. strcat(pathToNewContent, newFiles.fileList[fileListIndex].fullPathToFile);
  1537. sprintf( query, "SELECT fileID from FileVersionHistory WHERE applicationID=%i AND filename=$1::text AND createFile=TRUE;", applicationID );
  1538. outTemp[0]=hardDriveFilename;
  1539. outLengths[0]=(int)strlen(hardDriveFilename);
  1540. formats[0]=PQEXECPARAM_FORMAT_TEXT;
  1541. //sqlCommandMutex.Lock();
  1542. fileRows = PQexecParams(pgConn, query,1,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_TEXT);
  1543. //sqlCommandMutex.Unlock();
  1544. if (IsResultSuccessful(fileRows, true)==false)
  1545. {
  1546. newFiles.Clear();
  1547. PQclear(fileRows);
  1548. RakAssert(0);
  1549. return false;
  1550. }
  1551. fileIDColumnIndex = PQfnumber( fileRows, "fileID" );
  1552. numRows = PQntuples(fileRows);
  1553. outTemp[0]=hardDriveFilename;
  1554. outLengths[0]=(int)strlen(hardDriveFilename);
  1555. /*
  1556. FILE *fp = fopen(pathToNewContent, "rb");
  1557. if (fp==0)
  1558. {
  1559. newFiles.Clear();
  1560. PQclear(fileRows);
  1561. RakAssert(0);
  1562. return false;
  1563. }
  1564. newContent =(char*) rakMalloc_Ex( hardDriveDataLength, _FILE_AND_LINE_ );
  1565. fread(newContent,1,hardDriveDataLength,fp);
  1566. fclose(fp);
  1567. */
  1568. // Create new patches for every create version
  1569. for (rowIndex=0; rowIndex < numRows; rowIndex++ )
  1570. {
  1571. fileIDLength=PQgetlength(fileRows, rowIndex, fileIDColumnIndex);
  1572. fileID=PQgetvalue(fileRows, rowIndex, fileIDColumnIndex);
  1573. formats[0]=PQEXECPARAM_FORMAT_TEXT;
  1574. outTemp[0]=fileID;
  1575. outLengths[0]=fileIDLength;
  1576. // The last query handled all the relevant comparisons
  1577. sprintf(query, "SELECT pathToContent from FileVersionHistory WHERE fileID=$1::int;" );
  1578. //sqlCommandMutex.Lock();
  1579. result = PQexecParams(pgConn, query,1,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  1580. //sqlCommandMutex.Unlock();
  1581. if (IsResultSuccessful(result, true)==false)
  1582. {
  1583. // rakFree_Ex(newContent, _FILE_AND_LINE_);
  1584. Rollback();
  1585. newFiles.Clear();
  1586. PQclear(result);
  1587. return false;
  1588. }
  1589. int numContent = PQntuples(result);
  1590. if( numContent > 1 || numContent == 0 )
  1591. {
  1592. // rakFree_Ex(newContent, _FILE_AND_LINE_);
  1593. Rollback();
  1594. newFiles.Clear();
  1595. PQclear(result);
  1596. PQclear(fileRows);
  1597. RakAssert(0);
  1598. return false;
  1599. }
  1600. formats[0] = PQEXECPARAM_FORMAT_TEXT;
  1601. contentColumnIndex = PQfnumber(result, "pathToContent");
  1602. // contentLength=PQgetlength(result, 0, contentColumnIndex);
  1603. char *pathToOldContent;
  1604. pathToOldContent=PQgetvalue(result, 0, contentColumnIndex);
  1605. if (strcmp(pathToNewContent, pathToOldContent)==0)
  1606. {
  1607. // rakFree_Ex(newContent, _FILE_AND_LINE_);
  1608. sprintf(lastError,"New file version cannot have the same path as the old file version. Path=%s.\n", pathToNewContent);
  1609. Rollback();
  1610. newFiles.Clear();
  1611. PQclear(result);
  1612. PQclear(fileRows);
  1613. return false;
  1614. }
  1615. printf("%i/%i.%i/%i DIFF from %s to %s ...", fileListIndex+1, newFiles.fileList.Size(), rowIndex+1, numRows, pathToOldContent, pathToNewContent);
  1616. int patchAlgorithm;
  1617. int makePatchResult=MakePatch(pathToOldContent, pathToNewContent, &patch, &patchLength, &patchAlgorithm);
  1618. if (makePatchResult < 0 || makePatchResult == 2)
  1619. {
  1620. strcpy(lastError,"MakePatch failed.\n");
  1621. Rollback();
  1622. newFiles.Clear();
  1623. PQclear(result);
  1624. PQclear(fileRows);
  1625. return false;
  1626. }
  1627. if (makePatchResult ==1)
  1628. {
  1629. PQclear(result);
  1630. strcpy(lastError,"MakePatch skipped - No old version on disk.\n");
  1631. }
  1632. else
  1633. {
  1634. /*
  1635. if (CreatePatch(oldContent, contentLength, newContent, hardDriveDataLength, &patch, &patchLength)==false)
  1636. {
  1637. rakFree_Ex(oldContent, _FILE_AND_LINE_);
  1638. rakFree_Ex(newContent, _FILE_AND_LINE_);
  1639. strcpy(lastError,"CreatePatch failed.\n");
  1640. Rollback();
  1641. newFiles.Clear();
  1642. PQclear(result);
  1643. PQclear(fileRows);
  1644. return false;
  1645. }
  1646. */
  1647. // rakFree_Ex(oldContent, _FILE_AND_LINE_);
  1648. // oldContent=0;
  1649. // outTemp[0]=fileID;
  1650. // outLengths[0]=fileIDLength;
  1651. // outTemp[1]=patch;
  1652. // outLengths[1]=patchLength;
  1653. outTemp[0]=patch;
  1654. outLengths[0]=patchLength;
  1655. //sqlCommandMutex.Lock();
  1656. formats[0]=PQEXECPARAM_FORMAT_BINARY;
  1657. char buff[256];
  1658. sprintf(buff, "UPDATE FileVersionHistory SET patch=$1::bytea, patchAlgorithm=%i where fileID=%s;", patchAlgorithm, fileID);
  1659. uploadResult = PQexecParams(pgConn, buff, 1,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  1660. // Done with this patch data
  1661. delete [] patch;
  1662. //sqlCommandMutex.Unlock();
  1663. if (IsResultSuccessful(uploadResult, true)==false)
  1664. {
  1665. // rakFree_Ex(newContent, _FILE_AND_LINE_);
  1666. Rollback();
  1667. newFiles.Clear();
  1668. PQclear(result);
  1669. return false;
  1670. }
  1671. PQclear(result);
  1672. PQclear(uploadResult);
  1673. printf(" Done.\n");
  1674. }
  1675. }
  1676. PQclear(fileRows);
  1677. // rakFree_Ex(newContent, _FILE_AND_LINE_);
  1678. // newContent=0;
  1679. // Add totally new files
  1680. sprintf(query, "INSERT INTO FileVersionHistory (applicationID, filename, fileLength, pathToContent, contentHash, createFile, changeSetID, userName) "
  1681. "VALUES ("
  1682. "%i,"
  1683. "$1::text,"
  1684. "%i,"
  1685. "$2::text,"
  1686. "$3::bytea,"
  1687. "TRUE,"
  1688. "%i,"
  1689. "'%s'"
  1690. ");", applicationID, hardDriveDataLength, changeSetId, GetEscapedString(userName).C_String());
  1691. outTemp[0]=hardDriveFilename;
  1692. outTemp[1]=pathToNewContent;
  1693. outTemp[2]=hardDriveHash;
  1694. outLengths[0]=(int)strlen(hardDriveFilename);
  1695. outLengths[1]=(int)strlen(pathToNewContent);
  1696. outLengths[2]=HASH_LENGTH;
  1697. formats[0]=PQEXECPARAM_FORMAT_TEXT;
  1698. formats[1]=PQEXECPARAM_FORMAT_TEXT;
  1699. RakAssert(formats[2]==PQEXECPARAM_FORMAT_BINARY);
  1700. // Upload the new file
  1701. //sqlCommandMutex.Lock();
  1702. uploadResult = PQexecParams(pgConn, query,3,0,outTemp,outLengths,formats,PQEXECPARAM_FORMAT_BINARY);
  1703. //sqlCommandMutex.Unlock();
  1704. if( !uploadResult )
  1705. {
  1706. // Libpq had a problem inserting the file to the table. Most likely due to it running out of
  1707. // memory or a buffer being too small.
  1708. RakAssert( 0 );
  1709. newFiles.Clear();
  1710. PQclear(result);
  1711. return false;
  1712. }
  1713. if (IsResultSuccessful(uploadResult, true)==false)
  1714. {
  1715. newFiles.Clear();
  1716. PQclear(uploadResult);
  1717. PQclear(result);
  1718. return false;
  1719. }
  1720. // Clear the upload result
  1721. PQclear(uploadResult);
  1722. }
  1723. printf("DB COMMIT\n");
  1724. // If ExecuteBlockingCommand fails then it does a rollback
  1725. //sqlCommandMutex.Lock();
  1726. if (ExecuteBlockingCommand("COMMIT;", &result, true)==false)
  1727. {
  1728. //sqlCommandMutex.Unlock();
  1729. printf("COMMIT Failed!\n");
  1730. PQclear(result);
  1731. return false;
  1732. }
  1733. //sqlCommandMutex.Unlock();
  1734. PQclear(result);
  1735. printf("COMMIT Success\n");
  1736. return true;
  1737. }
  1738. int AutopatcherPostgreRepository2::MakePatch(const char *oldFile, const char *newFile, char **patch, unsigned int *patchLength, int *patchAlgorithm)
  1739. {
  1740. *patchAlgorithm=0;
  1741. FILE *fpOld = fopen(oldFile, "rb");
  1742. if (fpOld==0)
  1743. return 1;
  1744. fseek(fpOld, 0, SEEK_END);
  1745. int contentLengthOld = ftell(fpOld);
  1746. FILE *fpNew = fopen(newFile, "rb");
  1747. if (fpNew==0)
  1748. {
  1749. fclose(fpOld);
  1750. return 2;
  1751. }
  1752. fseek(fpNew, 0, SEEK_END);
  1753. int contentLengthNew = ftell(fpNew);
  1754. bool b = MakePatchBSDiff(fpOld, contentLengthOld, fpNew, contentLengthNew, patch, patchLength);
  1755. fclose(fpOld);
  1756. fclose(fpNew);
  1757. if (b==false)
  1758. return -1;
  1759. return 0;
  1760. }
  1761. bool AutopatcherPostgreRepository2::MakePatchBSDiff(FILE *fpOld, int contentLengthOld, FILE *fpNew, int contentLengthNew, char **patch, unsigned int *patchLength)
  1762. {
  1763. char *newContent, *oldContent;
  1764. oldContent = (char*) rakMalloc_Ex(contentLengthOld, _FILE_AND_LINE_);
  1765. RakAssert(oldContent);
  1766. fseek(fpOld, 0, SEEK_SET);
  1767. fread(oldContent, contentLengthOld, 1, fpOld);
  1768. newContent = (char*) rakMalloc_Ex(contentLengthNew, _FILE_AND_LINE_);
  1769. RakAssert(newContent);
  1770. fseek(fpNew, 0, SEEK_SET);
  1771. fread(newContent, contentLengthNew, 1, fpNew);
  1772. bool b = CreatePatch(oldContent, contentLengthOld, newContent, contentLengthNew, patch, patchLength);
  1773. if (b==false)
  1774. {
  1775. printf(
  1776. "AutopatcherPostgreRepository2::MakePatchBSDiff failed.\n"
  1777. "contentLengthOld=%i\n"
  1778. "contentLengthNew=%i\n",
  1779. contentLengthOld, contentLengthNew
  1780. );
  1781. }
  1782. rakFree_Ex(oldContent, _FILE_AND_LINE_);
  1783. rakFree_Ex(newContent, _FILE_AND_LINE_);
  1784. return b;
  1785. }
  1786. const char *AutopatcherPostgreRepository::GetLastError(void) const
  1787. {
  1788. return PostgreSQLInterface::GetLastError();
  1789. }
  1790. unsigned int AutopatcherPostgreRepository::GetPatchPart( const char *filename, unsigned int startReadBytes, unsigned int numBytesToRead, void *preallocatedDestination, FileListNodeContext context)
  1791. {
  1792. PGresult *result;
  1793. char query[512];
  1794. char *patch, *contentHash;
  1795. int patchLength;
  1796. bool firstBlock=startReadBytes==0;
  1797. if (firstBlock)
  1798. sprintf(query, "SELECT substring(patch from %i for %i) FROM FileVersionHistory WHERE fileId=%i;", startReadBytes+1,numBytesToRead-HASH_LENGTH,context.flnc_extraData3);
  1799. else
  1800. sprintf(query, "SELECT substring(patch from %i for %i) FROM FileVersionHistory WHERE fileId=%i;", startReadBytes+1-HASH_LENGTH,numBytesToRead,context.flnc_extraData3);
  1801. // CREATE NEW CONNECTION JUST FOR THIS QUERY
  1802. // This is because the autopatcher is sharing this class, but this is called from multiple threads and mysql is not threadsafe
  1803. filePartConnectionMutex.Lock();
  1804. if (filePartConnection==0)
  1805. filePartConnection=PQconnectdb(_conninfo);
  1806. result = PQexecParams(filePartConnection, query,0,0,0,0,0,PQEXECPARAM_FORMAT_BINARY);
  1807. if (IsResultSuccessful(result, true)==false)
  1808. {
  1809. PQclear(result);
  1810. return -1;
  1811. }
  1812. int patchColumnIndex = PQfnumber(result, "substring");
  1813. patch = PQgetvalue(result, 0, patchColumnIndex);
  1814. patchLength = PQgetlength(result, 0, patchColumnIndex);
  1815. if (firstBlock)
  1816. {
  1817. PGresult *result2;
  1818. char query2[512];
  1819. sprintf(query2, "SELECT contentHash FROM FileVersionHistory WHERE fileId=%i;", context.flnc_extraData1);
  1820. result2 = PQexecParams(filePartConnection, query2,0,0,0,0,0,PQEXECPARAM_FORMAT_BINARY);
  1821. int contentHashColumnIndex = PQfnumber(result2, "contentHash");
  1822. contentHash = PQgetvalue(result2, 0, contentHashColumnIndex);
  1823. memcpy(preallocatedDestination, contentHash, HASH_LENGTH);
  1824. memcpy((char*) preallocatedDestination+HASH_LENGTH, patch, patchLength);
  1825. PQclear(result);
  1826. PQclear(result2);
  1827. filePartConnectionMutex.Unlock();
  1828. return patchLength+HASH_LENGTH;
  1829. }
  1830. else
  1831. {
  1832. memcpy(preallocatedDestination,patch,patchLength);
  1833. PQclear(result);
  1834. filePartConnectionMutex.Unlock();
  1835. return patchLength;
  1836. }
  1837. }
  1838. unsigned int AutopatcherPostgreRepository::GetFilePart( const char *filename, unsigned int startReadBytes, unsigned int numBytesToRead, void *preallocatedDestination, FileListNodeContext context)
  1839. {
  1840. if (context.op==PC_HASH_1_WITH_PATCH)
  1841. {
  1842. return GetPatchPart(filename, startReadBytes, numBytesToRead, preallocatedDestination, context);
  1843. }
  1844. else
  1845. {
  1846. PGresult *result;
  1847. char query[512];
  1848. char *content;
  1849. int contentLength;
  1850. // Seems that substring is 1 based for its index, so add 1 to startReadBytes
  1851. sprintf(query, "SELECT substring(content from %i for %i) FROM FileVersionHistory WHERE fileId=%i;", startReadBytes+1,numBytesToRead,context.flnc_extraData1);
  1852. // CREATE NEW CONNECTION JUST FOR THIS QUERY
  1853. // This is because the autopatcher is sharing this class, but this is called from multiple threads and mysql is not threadsafe
  1854. filePartConnectionMutex.Lock();
  1855. if (filePartConnection==0)
  1856. filePartConnection=PQconnectdb(_conninfo);
  1857. result = PQexecParams(filePartConnection, query,0,0,0,0,0,PQEXECPARAM_FORMAT_BINARY);
  1858. content = PQgetvalue(result, 0, 0);
  1859. contentLength=PQgetlength(result, 0, 0);
  1860. memcpy(preallocatedDestination,content,contentLength);
  1861. PQclear(result);
  1862. filePartConnectionMutex.Unlock();
  1863. return contentLength;
  1864. }
  1865. }
  1866. unsigned int AutopatcherPostgreRepository2::GetFilePart( const char *filename, unsigned int startReadBytes, unsigned int numBytesToRead, void *preallocatedDestination, FileListNodeContext context)
  1867. {
  1868. if (context.op==PC_HASH_1_WITH_PATCH)
  1869. {
  1870. return GetPatchPart(filename, startReadBytes, numBytesToRead, preallocatedDestination, context);
  1871. }
  1872. else
  1873. {
  1874. PGresult *result;
  1875. char query[512];
  1876. unsigned int bytesRead;
  1877. RakAssert(context.op==PC_WRITE_FILE)
  1878. sprintf(query, "SELECT pathToContent FROM FileVersionHistory WHERE fileId=%i;", context.flnc_extraData1);
  1879. bool res = ExecuteBlockingCommand(query, &result, true);
  1880. char *path;
  1881. path = PQgetvalue(result,0,0);
  1882. FILE *fp = fopen(path, "rb");
  1883. fseek(fp, 0, SEEK_END);
  1884. unsigned int fileLength = ftell(fp);
  1885. fseek(fp, startReadBytes, SEEK_SET);
  1886. if (startReadBytes+numBytesToRead>fileLength)
  1887. bytesRead=fileLength-startReadBytes;
  1888. else
  1889. bytesRead=numBytesToRead;
  1890. bytesRead=(unsigned int) fread(preallocatedDestination,1,bytesRead,fp);
  1891. fclose(fp);
  1892. PQclear(result);
  1893. return bytesRead;
  1894. }
  1895. }
  1896. const int AutopatcherPostgreRepository::GetIncrementalReadChunkSize(void) const
  1897. {
  1898. return 262144*4*16;
  1899. }
粤ICP备19079148号