Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

SQLitePlugin.m 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. /*
  2. * Copyright (c) 2012-present Christopher J. Brody (aka Chris Brody)
  3. * Copyright (C) 2011 Davide Bertola
  4. *
  5. * This library is available under the terms of the MIT License (2008).
  6. * See http://opensource.org/licenses/alphabetical for full text.
  7. */
  8. #import "SQLitePlugin.h"
  9. #import "sqlite3.h"
  10. // Defines Macro to only log lines when in DEBUG mode
  11. #ifdef DEBUG
  12. # define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
  13. #else
  14. # define DLog(...)
  15. #endif
  16. #if !__has_feature(objc_arc)
  17. # error "Missing objc_arc feature"
  18. #endif
  19. // CustomPSPDFThreadSafeMutableDictionary interface copied from
  20. // CustomPSPDFThreadSafeMutableDictionary.m:
  21. //
  22. // Dictionary-Subclasss whose primitive operations are thread safe.
  23. @interface CustomPSPDFThreadSafeMutableDictionary : NSMutableDictionary
  24. @end
  25. @implementation SQLitePlugin
  26. @synthesize openDBs;
  27. @synthesize appDBPaths;
  28. -(void)pluginInitialize
  29. {
  30. DLog(@"Initializing SQLitePlugin");
  31. {
  32. openDBs = [CustomPSPDFThreadSafeMutableDictionary dictionaryWithCapacity:0];
  33. appDBPaths = [NSMutableDictionary dictionaryWithCapacity:0];
  34. NSString *docs = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex: 0];
  35. DLog(@"Detected docs path: %@", docs);
  36. [appDBPaths setObject: docs forKey:@"docs"];
  37. NSString *libs = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex: 0];
  38. DLog(@"Detected Library path: %@", libs);
  39. [appDBPaths setObject: libs forKey:@"libs"];
  40. NSString *nosync = [libs stringByAppendingPathComponent:@"LocalDatabase"];
  41. NSError *err;
  42. // GENERAL NOTE: no `nosync` directory path entry to be added
  43. // to appDBPaths map in case of any isses creating the
  44. // required directory or setting the resource value for
  45. // NSURLIsExcludedFromBackupKey
  46. //
  47. // This is to avoid potential for issue raised here:
  48. // https://github.com/xpbrew/cordova-sqlite-storage/issues/907
  49. if ([[NSFileManager defaultManager] fileExistsAtPath: nosync])
  50. {
  51. DLog(@"no cloud sync directory already exists at path: %@", nosync);
  52. }
  53. else
  54. {
  55. if ([[NSFileManager defaultManager] createDirectoryAtPath: nosync withIntermediateDirectories:NO attributes: nil error:&err])
  56. {
  57. DLog(@"no cloud sync directory created with path: %@", nosync);
  58. }
  59. else
  60. {
  61. // STOP HERE & LOG WITH INTERNAL PLUGIN ERROR:
  62. NSLog(@"INTERNAL PLUGIN ERROR: could not create no cloud sync directory at path: %@", nosync);
  63. return;
  64. }
  65. }
  66. {
  67. {
  68. // Set the resource value for NSURLIsExcludedFromBackupKey
  69. NSURL *nosyncURL = [ NSURL fileURLWithPath: nosync];
  70. if (![nosyncURL setResourceValue: [NSNumber numberWithBool: YES] forKey: NSURLIsExcludedFromBackupKey error: &err])
  71. {
  72. // STOP HERE & LOG WITH INTERNAL PLUGIN ERROR:
  73. NSLog(@"INTERNAL PLUGIN ERROR: error setting nobackup flag in LocalDatabase directory: %@", err);
  74. return;
  75. }
  76. // now ready to add `nosync` entry to appDBPaths:
  77. DLog(@"no cloud sync at path: %@", nosync);
  78. [appDBPaths setObject: nosync forKey:@"nosync"];
  79. }
  80. }
  81. }
  82. }
  83. -(id) getDBPath:(NSString *)dbFile at:(NSString *)atkey {
  84. if (dbFile == NULL) {
  85. return NULL;
  86. }
  87. NSString *dbdir = [appDBPaths objectForKey:atkey];
  88. if (dbdir == NULL) {
  89. // INTERNAL PLUGIN ERROR:
  90. return NULL;
  91. }
  92. NSString *dbPath = [dbdir stringByAppendingPathComponent: dbFile];
  93. return dbPath;
  94. }
  95. -(void)echoStringValue: (CDVInvokedUrlCommand*)command
  96. {
  97. CDVPluginResult * pluginResult = nil;
  98. NSMutableDictionary * options = [command.arguments objectAtIndex:0];
  99. NSString * string_value = [options objectForKey:@"value"];
  100. DLog(@"echo string value: %@", string_value);
  101. pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:string_value];
  102. [self.commandDelegate sendPluginResult:pluginResult callbackId: command.callbackId];
  103. }
  104. -(void)open: (CDVInvokedUrlCommand*)command
  105. {
  106. [self.commandDelegate runInBackground:^{
  107. [self openNow: command];
  108. }];
  109. }
  110. -(void)openNow: (CDVInvokedUrlCommand*)command
  111. {
  112. CDVPluginResult* pluginResult = nil;
  113. NSMutableDictionary *options = [command.arguments objectAtIndex:0];
  114. NSString *dbfilename = [options objectForKey:@"name"];
  115. NSString *dblocation = [options objectForKey:@"dblocation"];
  116. if (dblocation == NULL) dblocation = @"docs";
  117. // DLog(@"using db location: %@", dblocation);
  118. NSString *dbname = [self getDBPath:dbfilename at:dblocation];
  119. if (!sqlite3_threadsafe()) {
  120. // INTERNAL PLUGIN ERROR:
  121. NSLog(@"INTERNAL PLUGIN ERROR: sqlite3_threadsafe() returns false value");
  122. pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: @"INTERNAL PLUGIN ERROR: sqlite3_threadsafe() returns false value"];
  123. [self.commandDelegate sendPluginResult:pluginResult callbackId: command.callbackId];
  124. return;
  125. } else if (dbname == NULL) {
  126. // INTERNAL PLUGIN ERROR - NOT EXPECTED:
  127. NSLog(@"INTERNAL PLUGIN ERROR (NOT EXPECTED): open with database name missing");
  128. pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: @"INTERNAL PLUGIN ERROR: open with database name missing"];
  129. [self.commandDelegate sendPluginResult:pluginResult callbackId: command.callbackId];
  130. return;
  131. } else {
  132. NSValue *dbPointer = [openDBs objectForKey:dbfilename];
  133. if (dbPointer != NULL) {
  134. // NO LONGER EXPECTED due to BUG 666 workaround solution:
  135. // DLog(@"Reusing existing database connection for db name %@", dbfilename);
  136. NSLog(@"INTERNAL PLUGIN ERROR: database already open for db name: %@ (db file name: %@)", dbname, dbfilename);
  137. pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: @"INTERNAL PLUGIN ERROR: database already open"];
  138. [self.commandDelegate sendPluginResult:pluginResult callbackId: command.callbackId];
  139. return;
  140. }
  141. @synchronized(self) {
  142. const char *name = [dbname UTF8String];
  143. sqlite3 *db;
  144. DLog(@"open full db path: %@", dbname);
  145. if (sqlite3_open(name, &db) != SQLITE_OK) {
  146. pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Unable to open DB"];
  147. [self.commandDelegate sendPluginResult:pluginResult callbackId: command.callbackId];
  148. return;
  149. } else {
  150. sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, NULL);
  151. // for SQLCipher version:
  152. // NSString *dbkey = [options objectForKey:@"key"];
  153. // const char *key = NULL;
  154. // if (dbkey != NULL) key = [dbkey UTF8String];
  155. // if (key != NULL) sqlite3_key(db, key, strlen(key));
  156. // Attempt to read the SQLite master table [to support SQLCipher version]:
  157. if(sqlite3_exec(db, (const char*)"SELECT count(*) FROM sqlite_master;", NULL, NULL, NULL) == SQLITE_OK) {
  158. dbPointer = [NSValue valueWithPointer:db];
  159. [openDBs setObject: dbPointer forKey: dbfilename];
  160. pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:@"Database opened"];
  161. } else {
  162. pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Unable to open DB with key"];
  163. // XXX TODO: close the db handle & [perhaps] remove from openDBs!!
  164. }
  165. }
  166. }
  167. }
  168. [self.commandDelegate sendPluginResult:pluginResult callbackId: command.callbackId];
  169. // DLog(@"open cb finished ok");
  170. }
  171. -(void) close: (CDVInvokedUrlCommand*)command
  172. {
  173. [self.commandDelegate runInBackground:^{
  174. [self closeNow: command];
  175. }];
  176. }
  177. -(void)closeNow: (CDVInvokedUrlCommand*)command
  178. {
  179. CDVPluginResult* pluginResult = nil;
  180. NSMutableDictionary *options = [command.arguments objectAtIndex:0];
  181. NSString *dbFileName = [options objectForKey:@"path"];
  182. if (dbFileName == NULL) {
  183. // Should not happen:
  184. DLog(@"No db name specified for close");
  185. pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"INTERNAL PLUGIN ERROR: You must specify database path"];
  186. } else {
  187. NSValue *val = [openDBs objectForKey:dbFileName];
  188. sqlite3 *db = [val pointerValue];
  189. if (db == NULL) {
  190. // Should not happen:
  191. DLog(@"close: db name was not open: %@", dbFileName);
  192. pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"INTERNAL PLUGIN ERROR: Specified db was not open"];
  193. }
  194. else {
  195. DLog(@"close db name: %@", dbFileName);
  196. sqlite3_close (db);
  197. [openDBs removeObjectForKey:dbFileName];
  198. pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:@"DB closed"];
  199. }
  200. }
  201. [self.commandDelegate sendPluginResult:pluginResult callbackId: command.callbackId];
  202. }
  203. -(void) delete: (CDVInvokedUrlCommand*)command
  204. {
  205. [self.commandDelegate runInBackground:^{
  206. [self deleteNow: command];
  207. }];
  208. }
  209. -(void)deleteNow: (CDVInvokedUrlCommand*)command
  210. {
  211. CDVPluginResult* pluginResult = nil;
  212. NSMutableDictionary *options = [command.arguments objectAtIndex:0];
  213. NSString *dbFileName = [options objectForKey:@"path"];
  214. NSString *dblocation = [options objectForKey:@"dblocation"];
  215. if (dblocation == NULL) dblocation = @"docs";
  216. if (dbFileName==NULL) {
  217. // Should not happen:
  218. DLog(@"No db name specified for delete");
  219. pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"INTERNAL PLUGIN ERROR: You must specify database path"];
  220. } else {
  221. NSString *dbPath = [self getDBPath:dbFileName at:dblocation];
  222. if (dbPath == NULL) {
  223. // INTERNAL PLUGIN ERROR - NOT EXPECTED:
  224. NSLog(@"INTERNAL PLUGIN ERROR (NOT EXPECTED): delete with no valid database path found");
  225. pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: @"INTERNAL PLUGIN ERROR: delete with no valid database path found"];
  226. [self.commandDelegate sendPluginResult:pluginResult callbackId: command.callbackId];
  227. return;
  228. }
  229. if ([[NSFileManager defaultManager]fileExistsAtPath:dbPath]) {
  230. DLog(@"delete full db path: %@", dbPath);
  231. [[NSFileManager defaultManager]removeItemAtPath:dbPath error:nil];
  232. [openDBs removeObjectForKey:dbFileName];
  233. pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:@"DB deleted"];
  234. } else {
  235. DLog(@"delete: db was not found: %@", dbPath);
  236. pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"The database does not exist on that path"];
  237. }
  238. }
  239. [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
  240. }
  241. -(void) backgroundExecuteSqlBatch: (CDVInvokedUrlCommand*)command
  242. {
  243. [self.commandDelegate runInBackground:^{
  244. [self executeSqlBatchNow: command];
  245. }];
  246. }
  247. -(void) executeSqlBatchNow: (CDVInvokedUrlCommand*)command
  248. {
  249. NSMutableDictionary *options = [command.arguments objectAtIndex:0];
  250. NSMutableArray *results = [NSMutableArray arrayWithCapacity:0];
  251. NSMutableDictionary *dbargs = [options objectForKey:@"dbargs"];
  252. NSMutableArray *executes = [options objectForKey:@"executes"];
  253. CDVPluginResult* pluginResult;
  254. {
  255. for (NSMutableDictionary *dict in executes) {
  256. CDVPluginResult *result = [self executeSqlWithDict:dict andArgs:dbargs];
  257. if ([result.status intValue] == CDVCommandStatus_ERROR) {
  258. /* add error with result.message: */
  259. NSMutableDictionary *r = [NSMutableDictionary dictionaryWithCapacity:0];
  260. [r setObject:@"error" forKey:@"type"];
  261. [r setObject:result.message forKey:@"error"];
  262. [r setObject:result.message forKey:@"result"];
  263. [results addObject: r];
  264. } else {
  265. /* add result with result.message: */
  266. NSMutableDictionary *r = [NSMutableDictionary dictionaryWithCapacity:0];
  267. [r setObject:@"success" forKey:@"type"];
  268. [r setObject:result.message forKey:@"result"];
  269. [results addObject: r];
  270. }
  271. }
  272. pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:results];
  273. }
  274. [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
  275. }
  276. -(void) executeSql: (CDVInvokedUrlCommand*)command
  277. {
  278. NSMutableDictionary *options = [command.arguments objectAtIndex:0];
  279. NSMutableDictionary *dbargs = [options objectForKey:@"dbargs"];
  280. NSMutableDictionary *ex = [options objectForKey:@"ex"];
  281. CDVPluginResult * pluginResult = [self executeSqlWithDict: ex andArgs: dbargs];
  282. [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
  283. }
  284. -(CDVPluginResult*) executeSqlWithDict: (NSMutableDictionary*)options andArgs: (NSMutableDictionary*)dbargs
  285. {
  286. NSString *dbFileName = [dbargs objectForKey:@"dbname"];
  287. if (dbFileName == NULL) {
  288. return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"INTERNAL PLUGIN ERROR: You must specify database path"];
  289. }
  290. NSMutableArray *params = [options objectForKey:@"params"]; // optional
  291. NSValue *dbPointer = [openDBs objectForKey:dbFileName];
  292. if (dbPointer == NULL) {
  293. return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"INTERNAL PLUGIN ERROR: No such database, you must open it first"];
  294. }
  295. sqlite3 *db = [dbPointer pointerValue];
  296. NSString *sql = [options objectForKey:@"sql"];
  297. if (sql == NULL) {
  298. return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"INTERNAL PLUGIN ERROR: You must specify a sql query to execute"];
  299. }
  300. const char *sql_stmt = [sql UTF8String];
  301. NSDictionary *error = nil;
  302. sqlite3_stmt *statement;
  303. int result, i, column_type, count;
  304. int previousRowsAffected, nowRowsAffected, diffRowsAffected;
  305. long long previousInsertId, nowInsertId;
  306. BOOL keepGoing = YES;
  307. BOOL hasInsertId;
  308. NSMutableDictionary *resultSet = [NSMutableDictionary dictionaryWithCapacity:0];
  309. NSMutableArray *resultRows = [NSMutableArray arrayWithCapacity:0];
  310. NSMutableDictionary *entry;
  311. NSObject *columnValue;
  312. NSString *columnName;
  313. NSObject *insertId;
  314. NSObject *rowsAffected;
  315. hasInsertId = NO;
  316. previousRowsAffected = sqlite3_total_changes(db);
  317. previousInsertId = sqlite3_last_insert_rowid(db);
  318. if (sqlite3_prepare_v2(db, sql_stmt, -1, &statement, NULL) != SQLITE_OK) {
  319. error = [SQLitePlugin captureSQLiteErrorFromDb:db];
  320. keepGoing = NO;
  321. } else if (params != NULL) {
  322. for (int b = 0; b < params.count; b++) {
  323. result = [self bindStatement:statement withArg:[params objectAtIndex:b] atIndex:(b+1)];
  324. if (result != SQLITE_OK) {
  325. error = [SQLitePlugin captureSQLiteErrorFromDb:db];
  326. keepGoing = NO;
  327. break;
  328. }
  329. }
  330. }
  331. while (keepGoing) {
  332. result = sqlite3_step (statement);
  333. switch (result) {
  334. case SQLITE_ROW:
  335. i = 0;
  336. entry = [NSMutableDictionary dictionaryWithCapacity:0];
  337. count = sqlite3_column_count(statement);
  338. while (i < count) {
  339. columnValue = nil;
  340. columnName = [NSString stringWithFormat:@"%s", sqlite3_column_name(statement, i)];
  341. column_type = sqlite3_column_type(statement, i);
  342. switch (column_type) {
  343. case SQLITE_INTEGER:
  344. columnValue = [NSNumber numberWithLongLong: sqlite3_column_int64(statement, i)];
  345. break;
  346. case SQLITE_FLOAT:
  347. columnValue = [NSNumber numberWithDouble: sqlite3_column_double(statement, i)];
  348. break;
  349. case SQLITE_BLOB:
  350. case SQLITE_TEXT:
  351. columnValue = [[NSString alloc] initWithBytes:(char *)sqlite3_column_text(statement, i)
  352. length:sqlite3_column_bytes(statement, i)
  353. encoding:NSUTF8StringEncoding];
  354. break;
  355. case SQLITE_NULL:
  356. // just in case (should not happen):
  357. default:
  358. columnValue = [NSNull null];
  359. break;
  360. }
  361. if (columnValue) {
  362. [entry setObject:columnValue forKey:columnName];
  363. }
  364. i++;
  365. }
  366. [resultRows addObject:entry];
  367. break;
  368. case SQLITE_DONE:
  369. nowRowsAffected = sqlite3_total_changes(db);
  370. diffRowsAffected = nowRowsAffected - previousRowsAffected;
  371. rowsAffected = [NSNumber numberWithInt:diffRowsAffected];
  372. nowInsertId = sqlite3_last_insert_rowid(db);
  373. if (diffRowsAffected > 0 && nowInsertId != 0) {
  374. hasInsertId = YES;
  375. insertId = [NSNumber numberWithLongLong:sqlite3_last_insert_rowid(db)];
  376. }
  377. keepGoing = NO;
  378. break;
  379. default:
  380. error = [SQLitePlugin captureSQLiteErrorFromDb:db];
  381. keepGoing = NO;
  382. }
  383. }
  384. sqlite3_finalize (statement);
  385. if (error) {
  386. return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:error];
  387. }
  388. [resultSet setObject:resultRows forKey:@"rows"];
  389. [resultSet setObject:rowsAffected forKey:@"rowsAffected"];
  390. if (hasInsertId) {
  391. [resultSet setObject:insertId forKey:@"insertId"];
  392. }
  393. return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:resultSet];
  394. }
  395. -(int)bindStatement:(sqlite3_stmt *)statement withArg:(NSObject *)arg atIndex:(int)argIndex
  396. {
  397. int bindResult = SQLITE_ERROR;
  398. if ([arg isEqual:[NSNull null]]) {
  399. // bind null:
  400. bindResult = sqlite3_bind_null(statement, argIndex);
  401. } else if ([arg isKindOfClass:[NSNumber class]]) {
  402. // bind NSNumber (int64 or double):
  403. NSNumber *numberArg = (NSNumber *)arg;
  404. const char *numberType = [numberArg objCType];
  405. // Bind each number as INTEGER (long long int) or REAL (double):
  406. if (strcmp(numberType, @encode(int)) == 0 ||
  407. strcmp(numberType, @encode(long long int)) == 0) {
  408. bindResult = sqlite3_bind_int64(statement, argIndex, [numberArg longLongValue]);
  409. } else {
  410. bindResult = sqlite3_bind_double(statement, argIndex, [numberArg doubleValue]);
  411. }
  412. } else {
  413. // bind NSString (text):
  414. NSString *stringArg;
  415. if ([arg isKindOfClass:[NSString class]]) {
  416. stringArg = (NSString *)arg;
  417. } else {
  418. stringArg = [arg description]; // convert to text
  419. }
  420. // always bind text string as UTF-8 (sqlite does internal conversion if necessary):
  421. NSData *data = [stringArg dataUsingEncoding:NSUTF8StringEncoding];
  422. bindResult = sqlite3_bind_text(statement, argIndex, data.bytes, (int)data.length, SQLITE_TRANSIENT);
  423. }
  424. return bindResult;
  425. }
  426. -(void)dealloc
  427. {
  428. int i;
  429. NSArray *keys = [openDBs allKeys];
  430. NSValue *pointer;
  431. NSString *key;
  432. sqlite3 *db;
  433. /* close db the user forgot */
  434. for (i=0; i<[keys count]; i++) {
  435. key = [keys objectAtIndex:i];
  436. pointer = [openDBs objectForKey:key];
  437. db = [pointer pointerValue];
  438. sqlite3_close (db);
  439. }
  440. }
  441. +(NSDictionary *)captureSQLiteErrorFromDb:(struct sqlite3 *)db
  442. {
  443. int code = sqlite3_errcode(db);
  444. int webSQLCode = [SQLitePlugin mapSQLiteErrorCode:code];
  445. #if INCLUDE_SQLITE_ERROR_INFO
  446. int extendedCode = sqlite3_extended_errcode(db);
  447. #endif
  448. const char *message = sqlite3_errmsg(db);
  449. NSMutableDictionary *error = [NSMutableDictionary dictionaryWithCapacity:4];
  450. [error setObject:[NSNumber numberWithInt:webSQLCode] forKey:@"code"];
  451. [error setObject:[NSString stringWithUTF8String:message] forKey:@"message"];
  452. #if INCLUDE_SQLITE_ERROR_INFO
  453. [error setObject:[NSNumber numberWithInt:code] forKey:@"sqliteCode"];
  454. [error setObject:[NSNumber numberWithInt:extendedCode] forKey:@"sqliteExtendedCode"];
  455. [error setObject:[NSString stringWithUTF8String:message] forKey:@"sqliteMessage"];
  456. #endif
  457. return error;
  458. }
  459. +(int)mapSQLiteErrorCode:(int)code
  460. {
  461. // map the sqlite error code to
  462. // the websql error code
  463. switch(code) {
  464. case SQLITE_ERROR:
  465. return SYNTAX_ERR_;
  466. case SQLITE_FULL:
  467. return QUOTA_ERR;
  468. case SQLITE_CONSTRAINT:
  469. return CONSTRAINT_ERR;
  470. default:
  471. return UNKNOWN_ERR;
  472. }
  473. }
  474. @end /* vim: set expandtab : */