FMDatabaseAdditions.m 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. //
  2. // FMDatabaseAdditions.m
  3. // fmdb
  4. //
  5. // Created by August Mueller on 10/30/05.
  6. // Copyright 2005 Flying Meat Inc.. All rights reserved.
  7. //
  8. #import "FMDatabase.h"
  9. #import "FMDatabaseAdditions.h"
  10. #import "TargetConditionals.h"
  11. #if FMDB_SQLITE_STANDALONE
  12. #import <sqlite3/sqlite3.h>
  13. #else
  14. #import <sqlite3.h>
  15. #endif
  16. @interface FMDatabase (PrivateStuff)
  17. - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray * _Nullable)arrayArgs orDictionary:(NSDictionary * _Nullable)dictionaryArgs orVAList:(va_list)args;
  18. @end
  19. @implementation FMDatabase (FMDatabaseAdditions)
  20. #define RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(type, sel) \
  21. va_list args; \
  22. va_start(args, query); \
  23. FMResultSet *resultSet = [self executeQuery:query withArgumentsInArray:0x00 orDictionary:0x00 orVAList:args]; \
  24. va_end(args); \
  25. if (![resultSet next]) { return (type)0; } \
  26. type ret = [resultSet sel:0]; \
  27. [resultSet close]; \
  28. [resultSet setParentDB:nil]; \
  29. return ret;
  30. - (NSString *)stringForQuery:(NSString*)query, ... {
  31. RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSString *, stringForColumnIndex);
  32. }
  33. - (int)intForQuery:(NSString*)query, ... {
  34. RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(int, intForColumnIndex);
  35. }
  36. - (long)longForQuery:(NSString*)query, ... {
  37. RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(long, longForColumnIndex);
  38. }
  39. - (BOOL)boolForQuery:(NSString*)query, ... {
  40. RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(BOOL, boolForColumnIndex);
  41. }
  42. - (double)doubleForQuery:(NSString*)query, ... {
  43. RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(double, doubleForColumnIndex);
  44. }
  45. - (NSData*)dataForQuery:(NSString*)query, ... {
  46. RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSData *, dataForColumnIndex);
  47. }
  48. - (NSDate*)dateForQuery:(NSString*)query, ... {
  49. RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSDate *, dateForColumnIndex);
  50. }
  51. - (BOOL)tableExists:(NSString*)tableName {
  52. tableName = [tableName lowercaseString];
  53. FMResultSet *rs = [self executeQuery:@"select [sql] from sqlite_master where [type] = 'table' and lower(name) = ?", tableName];
  54. //if at least one next exists, table exists
  55. BOOL returnBool = [rs next];
  56. //close and free object
  57. [rs close];
  58. return returnBool;
  59. }
  60. /*
  61. get table with list of tables: result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING]
  62. check if table exist in database (patch from OZLB)
  63. */
  64. - (FMResultSet*)getSchema {
  65. //result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING]
  66. FMResultSet *rs = [self executeQuery:@"SELECT type, name, tbl_name, rootpage, sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type != 'meta' AND name NOT LIKE 'sqlite_%' ORDER BY tbl_name, type DESC, name"];
  67. return rs;
  68. }
  69. /*
  70. get table schema: result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER]
  71. */
  72. - (FMResultSet*)getTableSchema:(NSString*)tableName {
  73. //result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER]
  74. FMResultSet *rs = [self executeQuery:[NSString stringWithFormat: @"pragma table_info('%@')", tableName]];
  75. return rs;
  76. }
  77. - (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName {
  78. BOOL returnBool = NO;
  79. tableName = [tableName lowercaseString];
  80. columnName = [columnName lowercaseString];
  81. FMResultSet *rs = [self getTableSchema:tableName];
  82. //check if column is present in table schema
  83. while ([rs next]) {
  84. if ([[[rs stringForColumn:@"name"] lowercaseString] isEqualToString:columnName]) {
  85. returnBool = YES;
  86. break;
  87. }
  88. }
  89. //If this is not done FMDatabase instance stays out of pool
  90. [rs close];
  91. return returnBool;
  92. }
  93. - (uint32_t)applicationID {
  94. #if SQLITE_VERSION_NUMBER >= 3007017
  95. uint32_t r = 0;
  96. FMResultSet *rs = [self executeQuery:@"pragma application_id"];
  97. if ([rs next]) {
  98. r = (uint32_t)[rs longLongIntForColumnIndex:0];
  99. }
  100. [rs close];
  101. return r;
  102. #else
  103. NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil);
  104. if (self.logsErrors) NSLog(@"%@", errorMessage);
  105. return 0;
  106. #endif
  107. }
  108. - (void)setApplicationID:(uint32_t)appID {
  109. #if SQLITE_VERSION_NUMBER >= 3007017
  110. NSString *query = [NSString stringWithFormat:@"pragma application_id=%d", appID];
  111. FMResultSet *rs = [self executeQuery:query];
  112. [rs next];
  113. [rs close];
  114. #else
  115. NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil);
  116. if (self.logsErrors) NSLog(@"%@", errorMessage);
  117. #endif
  118. }
  119. #if TARGET_OS_MAC && !TARGET_OS_IPHONE
  120. - (NSString*)applicationIDString {
  121. #if SQLITE_VERSION_NUMBER >= 3007017
  122. NSString *s = NSFileTypeForHFSTypeCode([self applicationID]);
  123. assert([s length] == 6);
  124. s = [s substringWithRange:NSMakeRange(1, 4)];
  125. return s;
  126. #else
  127. NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil);
  128. if (self.logsErrors) NSLog(@"%@", errorMessage);
  129. return nil;
  130. #endif
  131. }
  132. - (void)setApplicationIDString:(NSString*)s {
  133. #if SQLITE_VERSION_NUMBER >= 3007017
  134. if ([s length] != 4) {
  135. NSLog(@"setApplicationIDString: string passed is not exactly 4 chars long. (was %ld)", [s length]);
  136. }
  137. [self setApplicationID:NSHFSTypeCodeFromFileType([NSString stringWithFormat:@"'%@'", s])];
  138. #else
  139. NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil);
  140. if (self.logsErrors) NSLog(@"%@", errorMessage);
  141. #endif
  142. }
  143. #endif
  144. - (uint32_t)userVersion {
  145. uint32_t r = 0;
  146. FMResultSet *rs = [self executeQuery:@"pragma user_version"];
  147. if ([rs next]) {
  148. r = (uint32_t)[rs longLongIntForColumnIndex:0];
  149. }
  150. [rs close];
  151. return r;
  152. }
  153. - (void)setUserVersion:(uint32_t)version {
  154. NSString *query = [NSString stringWithFormat:@"pragma user_version = %d", version];
  155. FMResultSet *rs = [self executeQuery:query];
  156. [rs next];
  157. [rs close];
  158. }
  159. #pragma clang diagnostic push
  160. #pragma clang diagnostic ignored "-Wdeprecated-implementations"
  161. - (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __attribute__ ((deprecated)) {
  162. return [self columnExists:columnName inTableWithName:tableName];
  163. }
  164. #pragma clang diagnostic pop
  165. - (BOOL)validateSQL:(NSString*)sql error:(NSError**)error {
  166. sqlite3_stmt *pStmt = NULL;
  167. BOOL validationSucceeded = YES;
  168. int rc = sqlite3_prepare_v2([self sqliteHandle], [sql UTF8String], -1, &pStmt, 0);
  169. if (rc != SQLITE_OK) {
  170. validationSucceeded = NO;
  171. if (error) {
  172. *error = [NSError errorWithDomain:NSCocoaErrorDomain
  173. code:[self lastErrorCode]
  174. userInfo:[NSDictionary dictionaryWithObject:[self lastErrorMessage]
  175. forKey:NSLocalizedDescriptionKey]];
  176. }
  177. }
  178. sqlite3_finalize(pStmt);
  179. return validationSucceeded;
  180. }
  181. @end