Index: src/MTXClient.m ================================================================== --- src/MTXClient.m +++ src/MTXClient.m @@ -216,10 +216,24 @@ exceptionWithStatusCode: statusCode response: response client: self]); return; } + + OFString *nextBatch = response[@"next_batch"]; + if (![nextBatch isKindOfClass: OFString.class]) { + block([OFInvalidServerReplyException exception]); + return; + } + + @try { + [_storage setNextBatch: nextBatch + forDeviceID: _deviceID]; + } @catch (id e) { + block(e); + return; + } block(nil); }]; objc_autoreleasePoolPop(pool); Index: src/MTXSQLite3Storage.m ================================================================== --- src/MTXSQLite3Storage.m +++ src/MTXSQLite3Storage.m @@ -25,10 +25,11 @@ #import "MTXSQLite3Storage.h" @implementation MTXSQLite3Storage { SL3Connection *_conn; + SL3PreparedStatement *_nextBatchSetStatement, *_nextBatchGetStatement; } + (instancetype)storageWithPath: (OFString *)path { return [[[self alloc] initWithPath: path] autorelease]; @@ -37,11 +38,27 @@ - (instancetype)initWithPath: (OFString *)path { self = [super init]; @try { + void *pool = objc_autoreleasePoolPush(); + _conn = [[SL3Connection alloc] initWithPath: path]; + + [self createTables]; + + _nextBatchSetStatement = [[_conn prepareStatement: + @"INSERT OR REPLACE INTO next_batch (\n" + @" device_id, next_batch\n" + @") VALUES (\n" + @" $device_id, $next_batch\n" + @")"] retain]; + _nextBatchGetStatement = [[_conn prepareStatement: + @"SELECT next_batch FROM next_batch\n" + @"WHERE device_id=$device_id"] retain]; + + objc_autoreleasePoolPop(pool); } @catch (id e) { [self release]; @throw e; } @@ -48,10 +65,55 @@ return self; } - (void)dealloc { + [_nextBatchSetStatement release]; + [_nextBatchGetStatement release]; [_conn release]; [super dealloc]; } + +- (void)createTables +{ + [_conn executeStatement: @"CREATE TABLE IF NOT EXISTS next_batch (\n" + @" device_id TEXT PRIMARY KEY,\n" + @" next_batch TEXT\n" + @")"]; +} + +- (void)setNextBatch: (OFString *)nextBatch + forDeviceID: (OFString *)deviceID +{ + void *pool = objc_autoreleasePoolPush(); + + [_nextBatchSetStatement reset]; + [_nextBatchSetStatement bindWithDictionary: @{ + @"$device_id": deviceID, + @"$next_batch": nextBatch + }]; + [_nextBatchSetStatement step]; + + objc_autoreleasePoolPop(pool); +} + +- (OFString *)nextBatchForDeviceID: (OFString *)deviceID +{ + void *pool = objc_autoreleasePoolPush(); + + [_nextBatchGetStatement reset]; + [_nextBatchGetStatement bindWithDictionary: @{ + @"$device_id": deviceID + }]; + + if (![_nextBatchGetStatement step]) + return nil; + + OFString *nextBatch = + [[_nextBatchGetStatement rowDictionary][@"next_batch"] retain]; + + objc_autoreleasePoolPop(pool); + + return [nextBatch autorelease]; +} @end Index: src/MTXStorage.h ================================================================== --- src/MTXStorage.h +++ src/MTXStorage.h @@ -26,8 +26,25 @@ /** * @brief A protocol for a storage to be used by @ref MTXClient. */ @protocol MTXStorage +/** + * @brief Stores the next batch for the specified device. + * + * @param nextBatch The next batch for the device + * @param deviceID The device for which to store the next batch + */ +- (void)setNextBatch: (OFString *)nextBatch + forDeviceID: (OFString *)deviceID; + +/** + * @brief Returns the next batch for the specified device. + * + * @param deviceID The device ID for which to return the next batch + * @return The next batch for the specified device, or `nil` if none is + * available. + */ +- (nullable OFString *)nextBatchForDeviceID: (OFString *)deviceID; @end OF_ASSUME_NONNULL_END