· 8 years ago · Jul 11, 2018, 05:28 PM
1//
2// DBOperations.m
3// Collect
4//
5// Created by Tyler Powers on 3/13/12.
6// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
7//
8
9#import "DBOperations.h"
10#import "FMDatabase.h"
11#import "FMDatabaseAdditions.h"
12#import "FMDatabasePool.h"
13#import "FMDatabaseQueue.h"
14
15#import "FileModel.h" //is this where this should be going?!
16#include <sqlite3.h>
17
18#define FMDBQuickCheck(SomeBool) { if (!(SomeBool)) { NSLog(@"Failure on line %d", __LINE__); abort(); } }
19
20@implementation DBOperations
21
22@synthesize currentDatabaseID;
23@synthesize error;
24
25- (id) init
26{
27 self = [super init];
28 if (self)
29 {
30 NSLog(@"Inited DBOperations");
31
32 fm = [[NSFileManager alloc] init];
33 dbFilesPath = @"";
34 currentDatabaseID = 0;
35 bytesReadSinceLastDBFile = 0;
36 readyForNewDB = YES;
37 bytesWrittenToBlob = 0;
38 }
39 return self;
40}
41
42- (void) setDatabaseFilesDirectory:(NSString *)directory
43{
44 // Check to make sure path exists!
45 if ([fm fileExistsAtPath:directory])
46 {
47 dbFilesPath = directory;
48 NSLog(@"Set the database files directory to %@", dbFilesPath);
49
50 NSString *controlDBPath = [directory stringByAppendingString:@"/library000.lib"];
51
52 if ([fm fileExistsAtPath:controlDBPath])
53 {
54 controlDB = [FMDatabase databaseWithPath:controlDBPath];
55
56 if (![controlDB open]) {
57 NSLog(@"Can't open the db!");
58 }
59
60 NSLog(@"%d: %@", [controlDB lastErrorCode], [controlDB lastErrorMessage]);
61 }
62 }
63 else
64 {
65 @throw [NSException exceptionWithName:@"InvalidDirectory" reason:@"SetDatabaseFilesDirectory was passed a non-existent path!" userInfo:nil];
66 }
67}
68
69- (void) buildControlDB
70{
71 NSLog(@"Creating control DB to hold listing of db files, stats, etc.");
72 controlDB = [self createNewDB];
73
74 [controlDB executeUpdate:@"create table dbFilesListing (filePath text)"];
75 [controlDB executeUpdate:@"create table stats (name text, value text)"];
76}
77
78- (FMDatabase *) createNewDB
79{
80 NSString *dbPath = [dbFilesPath stringByAppendingFormat:@"/library%03d.lib", currentDatabaseID];
81
82 NSLog(@"Creating a new DB at path %@ ...", dbPath);
83
84 if (![fm fileExistsAtPath:dbPath])
85 {
86 db = [FMDatabase databaseWithPath:dbPath];
87
88 if (![db open]) {
89 NSLog(@"Can't open the db!");
90 }
91
92 if ([db hadError]) {
93 NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);
94 }
95
96 currentDatabaseID++;
97 }
98 else
99 {
100 //@throw [NSException exceptionWithName:@"DBFileAlreadyExists" reason:@"A database is already at this location!" userInfo:nil];
101 NSLog(@"A database is already at this location!");
102 }
103
104 return db;
105}
106
107- (void) createNewContainerDB
108{
109 db = [self createNewDB];
110 [self createTablesForContainerDBs:db];
111 readyForNewDB = NO;
112 bytesReadSinceLastDBFile = 0;
113 [controlDB executeUpdate:@"insert into dbFilesListing (filePath) values (?)", [db databasePath]];
114 NSLog(@"Had to create new db at %@ ...ready now!", [db databasePath]);
115}
116
117- (void) createTablesForContainerDBs:(FMDatabase *)newDB
118{
119 [newDB executeUpdate:@"create table filesBlob (filePath text, fileData blob)"];
120 [newDB executeUpdate:@"create table fileAttribs (filePath text, attrib1 text, attrib2 text, attrib3 text)"];
121 [newDB executeUpdate:@"create table errors (filePath text, error text)"];
122}
123
124- (void) insertFileIntoCurrentDB:(FileModel *)file
125{
126 // Do some checking on current db file size, and decide if it's time to initialize a new sqlite db yet or not...
127 bytesReadSinceLastDBFile += [file.itemLength intValue];
128
129 if (readyForNewDB || bytesReadSinceLastDBFile >= 1879048192) //is our bytesReadSinceLastDB file above 1.75GB???
130 {
131 [self createNewContainerDB];
132 }
133
134 NSString *filePath = file.itemFullName;
135
136 if ([fm fileExistsAtPath:filePath])
137 {
138 NSDictionary *fileAttribs = [fm attributesOfItemAtPath:filePath error:nil];
139 int fileLength = [[fileAttribs objectForKey:NSFileSize] intValue];
140
141 [db executeUpdate:@"insert into fileAttribs (filePath, attrib1, attrib2, attrib3) values (?,?,?,?)", file.itemFullName, file.itemCreationTime, file.itemLength, file.itemExtension];
142
143 NSString *sql = @"insert into filesBlob (filePath, fileData) values (?,?)";
144
145 int rc;
146
147 NSLog(@"*** DB PATH: %@", [db databasePath]);
148
149 rc = sqlite3_open([[db databasePath] UTF8String], &dbHandleC);
150 if (rc)
151 {
152 fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(dbHandleC));
153 sqlite3_close(dbHandleC);
154 }
155
156 rc = sqlite3_prepare_v2(dbHandleC, [sql UTF8String], [sql length], &statementHandleC, 0);
157 if (SQLITE_OK != rc)
158 {
159 fprintf(stderr, "Error preparing statement: %s\n", sqlite3_errmsg(dbHandleC));
160 sqlite3_close(dbHandleC);
161 }
162
163 sqlite3_bind_text(statementHandleC, 1, [filePath UTF8String], -1, SQLITE_STATIC);
164 sqlite3_bind_zeroblob(statementHandleC, 2, fileLength);
165
166
167 BOOL retry;
168
169 do {
170 rc = sqlite3_step(statementHandleC);
171 retry = NO;
172
173 if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
174 // this will happen if the db is locked, like if we are doing an update or insert.
175 // in that case, retry the step... and maybe wait just 10 milliseconds.
176 retry = YES;
177 if (SQLITE_LOCKED == rc) {
178 rc = sqlite3_reset(statementHandleC);
179 if (rc != SQLITE_LOCKED) {
180 NSLog(@"Unexpected result from sqlite3_reset (%d) eu", rc);
181 }
182 }
183 usleep(20);
184 }
185 else if (SQLITE_DONE == rc) {
186 // all is well, let's return.
187 }
188 else if (SQLITE_ERROR == rc) {
189 NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(dbHandleC));
190 NSLog(@"DB Query: %@", sql);
191 }
192 else if (SQLITE_MISUSE == rc) {
193 // uh oh.
194 NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(dbHandleC));
195 NSLog(@"DB Query: %@", sql);
196 }
197 else {
198 // wtf?
199 NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(dbHandleC));
200 NSLog(@"DB Query: %@", sql);
201 }
202
203 } while (retry);
204
205 rc = sqlite3_blob_open(dbHandleC, "main", "filesBlob", "fileData", sqlite3_last_insert_rowid(dbHandleC), 1, &blobHandleC);
206 if (rc)
207 {
208 fprintf(stderr, "Error opening blob: %s\n", sqlite3_errmsg(dbHandleC));
209 sqlite3_close(dbHandleC);
210 }
211
212 // set up an input Stream and open it up!
213 iStream = [[NSInputStream alloc] initWithFileAtPath:filePath];
214 [iStream setDelegate:self];
215 [iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
216 forMode:NSDefaultRunLoopMode];
217 [iStream open];
218 [[NSRunLoop currentRunLoop] run]; //on a new thread so we must manually start the run loop (won't run until something is scheduled)
219
220 }
221 else
222 {
223 //NSLog(@"Could not find file: %@", file.itemFullName);
224 }
225}
226
227- (BOOL) addBytesFromFileInputStreamToBlob:(NSInputStream *)input
228{
229 uint8_t inputBuffer[1024];
230 while (!_error && input.hasBytesAvailable) {
231 NSInteger nRead = [input read: inputBuffer maxLength: sizeof(inputBuffer)];
232 if (nRead < 0) {
233 self.error = input.streamError;
234 return NO;
235 } else if (nRead == 0) {
236 break;
237 } else
238 {
239 int rc;
240 rc = sqlite3_blob_write(blobHandleC, inputBuffer, nRead, bytesWrittenToBlob);
241 bytesWrittenToBlob += nRead;
242
243 if (SQLITE_OK != rc)
244 {
245 fprintf(stderr, "Error writing to blob, rc: %d, %s\n", rc, sqlite3_errmsg(dbHandleC));
246 }
247 }
248 }
249 return YES;
250}
251
252- (void) closeCurrentDB
253{
254 NSLog(@"closing the db...");
255 [db close];
256}
257
258- (void) logFileNotFound:(FileModel *)file
259{
260 [db executeUpdate:@"insert into errors (?,?)", file.itemFullName, @"File not found!"];
261}
262
263- (NSMutableArray *) getContainerFileListing
264{
265 NSMutableArray *containerFileListing = [[NSMutableArray alloc] init];
266 FMResultSet *resultSet = [controlDB executeQuery:@"select filePath from dbFilesListing"];
267
268 while ([resultSet next]) {
269 NSLog(@"resultsSet: %@", [resultSet stringForColumn:@"filePath"]);
270 [containerFileListing addObject:[resultSet stringForColumn:@"filePath"]];
271 }
272
273 NSLog(@"getContainerFileListing %d: %@", [controlDB lastErrorCode], [controlDB lastErrorMessage]);
274
275 [resultSet close];
276 return containerFileListing;
277}
278
279- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
280{
281 NSLog(@"Handling BLOB INPUT NSStream Event! %lu", eventCode);
282
283 switch (eventCode)
284 {
285 case NSStreamEventNone:
286 {
287 NSLog(@"BLOB INPUT: NSStreamEventNone");
288 }
289 break;
290 case NSStreamEventOpenCompleted:
291 {
292 NSLog(@"BLOB INPUT: NSStreamEventOpenCompleted");
293 }
294 break;
295 case NSStreamEventHasBytesAvailable:
296 {
297 NSLog(@"BLOB INPUT: NSStreamEventHasBytesAvailable");
298 [self addBytesFromFileInputStreamToBlob:(NSInputStream*)stream];
299 }
300 break;
301 case NSStreamEventHasSpaceAvailable:
302 {
303 NSLog(@"BLOB INPUT: NSStreamEventHasSpaceAvailable");
304 }
305 break;
306 case NSStreamEventEndEncountered:
307 {
308 NSLog(@"BLOB INPUT: NSStreamEventEndEncountered");
309 [stream close];
310 [stream removeFromRunLoop:[NSRunLoop currentRunLoop]
311 forMode:NSDefaultRunLoopMode];
312 stream = nil;
313
314 bytesWrittenToBlob = 0;
315
316 sqlite3_finalize(statementHandleC);
317 sqlite3_blob_close(blobHandleC);
318 sqlite3_close(dbHandleC);
319 }
320 break;
321 case NSStreamEventErrorOccurred:
322 {
323 NSLog(@"***** BLOB INPUT - FUCK, an error occured writing the stream");
324 NSError *theError = [stream streamError];
325 NSAlert *theAlert = [[NSAlert alloc] init]; // modal delegate releases
326 [theAlert setMessageText:@"Error reading stream!"];
327 [theAlert setInformativeText:[NSString stringWithFormat:@"Error %i: %@",[theError code], [theError localizedDescription]]];
328 [theAlert addButtonWithTitle:@"OK"];
329 [theAlert beginSheetModalForWindow:[NSApp mainWindow]
330 modalDelegate:self
331 didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:)
332 contextInfo:nil];
333 [stream close];
334 }
335 break;
336 default:
337 break;
338 }
339}
340
341@end