单军华
2018-07-19 83b9d5c682b21d88133f24da0f94dd56bd79e687
screendisplay/Pods/FMDB/src/fmdb/FMDatabasePool.m
New file
@@ -0,0 +1,316 @@
//
//  FMDatabasePool.m
//  fmdb
//
//  Created by August Mueller on 6/22/11.
//  Copyright 2011 Flying Meat Inc. All rights reserved.
//
#if FMDB_SQLITE_STANDALONE
#import <sqlite3/sqlite3.h>
#else
#import <sqlite3.h>
#endif
#import "FMDatabasePool.h"
#import "FMDatabase.h"
@interface FMDatabasePool () {
    dispatch_queue_t    _lockQueue;
    NSMutableArray      *_databaseInPool;
    NSMutableArray      *_databaseOutPool;
}
- (void)pushDatabaseBackInPool:(FMDatabase*)db;
- (FMDatabase*)db;
@end
@implementation FMDatabasePool
@synthesize path=_path;
@synthesize delegate=_delegate;
@synthesize maximumNumberOfDatabasesToCreate=_maximumNumberOfDatabasesToCreate;
@synthesize openFlags=_openFlags;
+ (instancetype)databasePoolWithPath:(NSString *)aPath {
    return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]);
}
+ (instancetype)databasePoolWithURL:(NSURL *)url {
    return FMDBReturnAutoreleased([[self alloc] initWithPath:url.path]);
}
+ (instancetype)databasePoolWithPath:(NSString *)aPath flags:(int)openFlags {
    return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath flags:openFlags]);
}
+ (instancetype)databasePoolWithURL:(NSURL *)url flags:(int)openFlags {
    return FMDBReturnAutoreleased([[self alloc] initWithPath:url.path flags:openFlags]);
}
- (instancetype)initWithURL:(NSURL *)url flags:(int)openFlags vfs:(NSString *)vfsName {
    return [self initWithPath:url.path flags:openFlags vfs:vfsName];
}
- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName {
    self = [super init];
    if (self != nil) {
        _path               = [aPath copy];
        _lockQueue          = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL);
        _databaseInPool     = FMDBReturnRetained([NSMutableArray array]);
        _databaseOutPool    = FMDBReturnRetained([NSMutableArray array]);
        _openFlags          = openFlags;
        _vfsName            = [vfsName copy];
    }
    return self;
}
- (instancetype)initWithPath:(NSString *)aPath flags:(int)openFlags {
    return [self initWithPath:aPath flags:openFlags vfs:nil];
}
- (instancetype)initWithURL:(NSURL *)url flags:(int)openFlags {
    return [self initWithPath:url.path flags:openFlags vfs:nil];
}
- (instancetype)initWithPath:(NSString*)aPath {
    // default flags for sqlite3_open
    return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE];
}
- (instancetype)initWithURL:(NSURL *)url {
    return [self initWithPath:url.path];
}
- (instancetype)init {
    return [self initWithPath:nil];
}
+ (Class)databaseClass {
    return [FMDatabase class];
}
- (void)dealloc {
    _delegate = 0x00;
    FMDBRelease(_path);
    FMDBRelease(_databaseInPool);
    FMDBRelease(_databaseOutPool);
    FMDBRelease(_vfsName);
    if (_lockQueue) {
        FMDBDispatchQueueRelease(_lockQueue);
        _lockQueue = 0x00;
    }
#if ! __has_feature(objc_arc)
    [super dealloc];
#endif
}
- (void)executeLocked:(void (^)(void))aBlock {
    dispatch_sync(_lockQueue, aBlock);
}
- (void)pushDatabaseBackInPool:(FMDatabase*)db {
    if (!db) { // db can be null if we set an upper bound on the # of databases to create.
        return;
    }
    [self executeLocked:^() {
        if ([self->_databaseInPool containsObject:db]) {
            [[NSException exceptionWithName:@"Database already in pool" reason:@"The FMDatabase being put back into the pool is already present in the pool" userInfo:nil] raise];
        }
        [self->_databaseInPool addObject:db];
        [self->_databaseOutPool removeObject:db];
    }];
}
- (FMDatabase*)db {
    __block FMDatabase *db;
    [self executeLocked:^() {
        db = [self->_databaseInPool lastObject];
        BOOL shouldNotifyDelegate = NO;
        if (db) {
            [self->_databaseOutPool addObject:db];
            [self->_databaseInPool removeLastObject];
        }
        else {
            if (self->_maximumNumberOfDatabasesToCreate) {
                NSUInteger currentCount = [self->_databaseOutPool count] + [self->_databaseInPool count];
                if (currentCount >= self->_maximumNumberOfDatabasesToCreate) {
                    NSLog(@"Maximum number of databases (%ld) has already been reached!", (long)currentCount);
                    return;
                }
            }
            db = [[[self class] databaseClass] databaseWithPath:self->_path];
            shouldNotifyDelegate = YES;
        }
        //This ensures that the db is opened before returning
#if SQLITE_VERSION_NUMBER >= 3005000
        BOOL success = [db openWithFlags:self->_openFlags vfs:self->_vfsName];
#else
        BOOL success = [db open];
#endif
        if (success) {
            if ([self->_delegate respondsToSelector:@selector(databasePool:shouldAddDatabaseToPool:)] && ![self->_delegate databasePool:self shouldAddDatabaseToPool:db]) {
                [db close];
                db = 0x00;
            }
            else {
                //It should not get added in the pool twice if lastObject was found
                if (![self->_databaseOutPool containsObject:db]) {
                    [self->_databaseOutPool addObject:db];
                    if (shouldNotifyDelegate && [self->_delegate respondsToSelector:@selector(databasePool:didAddDatabase:)]) {
                        [self->_delegate databasePool:self didAddDatabase:db];
                    }
                }
            }
        }
        else {
            NSLog(@"Could not open up the database at path %@", self->_path);
            db = 0x00;
        }
    }];
    return db;
}
- (NSUInteger)countOfCheckedInDatabases {
    __block NSUInteger count;
    [self executeLocked:^() {
        count = [self->_databaseInPool count];
    }];
    return count;
}
- (NSUInteger)countOfCheckedOutDatabases {
    __block NSUInteger count;
    [self executeLocked:^() {
        count = [self->_databaseOutPool count];
    }];
    return count;
}
- (NSUInteger)countOfOpenDatabases {
    __block NSUInteger count;
    [self executeLocked:^() {
        count = [self->_databaseOutPool count] + [self->_databaseInPool count];
    }];
    return count;
}
- (void)releaseAllDatabases {
    [self executeLocked:^() {
        [self->_databaseOutPool removeAllObjects];
        [self->_databaseInPool removeAllObjects];
    }];
}
- (void)inDatabase:(void (^)(FMDatabase *db))block {
    FMDatabase *db = [self db];
    block(db);
    [self pushDatabaseBackInPool:db];
}
- (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block {
    BOOL shouldRollback = NO;
    FMDatabase *db = [self db];
    if (useDeferred) {
        [db beginDeferredTransaction];
    }
    else {
        [db beginTransaction];
    }
    block(db, &shouldRollback);
    if (shouldRollback) {
        [db rollback];
    }
    else {
        [db commit];
    }
    [self pushDatabaseBackInPool:db];
}
- (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
    [self beginTransaction:YES withBlock:block];
}
- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
    [self beginTransaction:NO withBlock:block];
}
- (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block {
#if SQLITE_VERSION_NUMBER >= 3007000
    static unsigned long savePointIdx = 0;
    NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++];
    BOOL shouldRollback = NO;
    FMDatabase *db = [self db];
    NSError *err = 0x00;
    if (![db startSavePointWithName:name error:&err]) {
        [self pushDatabaseBackInPool:db];
        return err;
    }
    block(db, &shouldRollback);
    if (shouldRollback) {
        // We need to rollback and release this savepoint to remove it
        [db rollbackToSavePointWithName:name error:&err];
    }
    [db releaseSavePointWithName:name error:&err];
    [self pushDatabaseBackInPool:db];
    return err;
#else
    NSString *errorMessage = NSLocalizedString(@"Save point functions require SQLite 3.7", nil);
    if (self.logsErrors) NSLog(@"%@", errorMessage);
    return [NSError errorWithDomain:@"FMDatabase" code:0 userInfo:@{NSLocalizedDescriptionKey : errorMessage}];
#endif
}
@end