Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>So, a couple of thoughts:</p> <ol> <li><p>As Prateek and Lefteris suggested, you should change the order of the <code>bind</code> calls so that they match the order of the fields in the SQL. They also start with an index of one, not zero. I've always found it odd that the index for <code>sqlite3_bind</code> calls start at one, whereas the index for <code>sqlite3_column</code> calls start at zero, but it's just the way it works.</p></li> <li><p>While it's probably fine, if you ever do an assertion, you should generally make sure you first (a) finalize any successfully prepared statements; and (b) close the database. You don't want to risk leaving your database in an inconsistent state.</p></li> <li><p>You probably should not release <code>sapContactId</code> or <code>sapCustId</code> here. You didn't <code>retain</code> or do anything to increase their retain count, so it's probably not prudent to be releasing them here. If you run your code through the static analyzer (press <kbd>shift</kbd>+<kbd>command</kbd>+<kbd>B</kbd> or choose "Analyze" from the product menu), you'll undoubtedly see suggestions/warnings there.</p></li> <li><p>As outlined in the official SQLite <a href="http://www.sqlite.org/cintro.html" rel="nofollow">An Introduction To the SQLite C/C++ Interface</a>, your sequence of <code>open</code>, <code>prepare_v2</code>, <code>step</code>, <code>finalize</code>, and <code>close</code> is perfectly correct. You do not need to call <code>sqlite3_reset</code>. The <a href="http://www.sqlite.org/c3ref/reset.html" rel="nofollow">purpose</a> of <code>reset</code> is to allow you to reuse a previous prepared <code>sqlite3_stmt</code>, but you're not doing that here, so <code>reset</code> is unnecessary here and achieves nothing.</p></li> <li><p>At one point in your comments, you said that you received an error "database locked". If you're still getting that having addressed the above items, let us know where you're getting it (at <code>open</code>? during the <code>prepare</code> of a SQL statement?), as there can be different sources of this problem. Certainly multi-threaded database operations can cause this. You need help us diagnose this by showing us precisely which line you're getting this locked message on, and describe what other database operations (beyond the code in your original question) that you might be doing, especially if you're doing anything in a background queue/thread.</p> <p>The challenge in solving "database locked" problems is that the problem invariably is not in the code in which you're experiencing the "database locked" error, but in some previous database interaction that you failed to handle properly. Technically, a failure to call <code>sqlite3_finalize</code> could cause the "database locked" problem, but in practice that's rarely the case. I see "database locked" errors usually occurring because of other issues, but we really need to know whether you're experiencing it during the opening of the database, or while trying to <code>prepare</code> or <code>step</code> a SQL statement. You need to tell us which for us to go further in the diagnosis. But, like I said, the problem undoubtedly rests in other/prior database interactions, so you might need to share more about what other database operations you're doing <em>prior</em> to the "database locked" error.</p></li> </ol> <hr> <p>If you're interested in a discussion regarding the rewrite of the code in your original question, see below.</p> <p>A minor suggestion, but I'd suggest altering your <code>initDatabase</code> method to make sure you log an error message if the <code>sqlite3_open</code> call fails, such as:</p> <pre><code>-(void)initDatabase { // Create a string containing the full path to the sqlite.db inside the documents folder NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:@"contact1.sqlite"]; // Check to see if the database file already exists bool databaseAlreadyExists = [[NSFileManager defaultManager] fileExistsAtPath:databasePath]; // Open the database and store the handle as a data member if (sqlite3_open([databasePath UTF8String], &amp;databaseHandle) == SQLITE_OK) { // Create the database if it doesn't yet exists in the file system if (!databaseAlreadyExists) { // Create the contactList table const char *sqlStatement = "CREATE TABLE IF NOT EXISTS contactList (sapCustId TEXT, sapContactId TEXT, record_ID NUMERIC PRIMARY KEY, timestamp TEXT)"; char *error; if (sqlite3_exec(databaseHandle, sqlStatement, NULL, NULL, &amp;error) == SQLITE_OK) { NSLog(@"Database and tables created."); } else { NSLog(@"%s: error creating table: %s", __FUNCTION__, sqlite3_errmsg(databaseHandle)); } } } else { NSLog(@"%s: error opening database: %s", __FUNCTION__, sqlite3_errmsg(databaseHandle)); } } </code></pre> <p>Second, you look at <code>insertRecord</code>:</p> <ul> <li><p>You want to fix the order that you use the four bind statements.</p></li> <li><p>You can also eliminate the redundant <code>if</code> statement. </p></li> <li><p>If you use assertions or return from the middle of a method doing database interactions, you always want to do a graceful cleanup. For example, you'll see if my prepare failed, I'll just close, but if the <code>step</code> failed, then I make sure to <code>finalize</code> and <code>close</code>. </p></li> <li><p>You probably should get rid of those inappropriate <code>release</code> statements, moving them to the right place in the code.</p></li> <li><p>You should change the method signature to have named parameters (because in the <a href="https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingMethods.html#//apple_ref/doc/uid/20001282-1001751-BCIJHEDH" rel="nofollow">General Rules</a> section of the <em>Coding Guidelines,</em> Apple says that we should name our method keywords.</p></li> </ul> <p>Thus:</p> <pre><code>- (void) insertRecord:(int)recordID sapCustId:(NSString *)sapCustId sapContactId:(NSString *)sapContactId timestamp:(NSString *)timestamp { [self initDatabase]; sqlite3_stmt *statement = nil; const char *sql_stmt = "INSERT INTO contactList (sapCustId, sapContactId, timestamp, record_ID) VALUES (?, ?, ?, ?)"; if(sqlite3_prepare_v2(databaseHandle, sql_stmt, -1, &amp;statement, NULL) != SQLITE_OK) { NSLog(@"%s: insert prepare error: '%s'", __FUNCTION__, sqlite3_errmsg(databaseHandle)); sqlite3_close(databaseHandle); NSAssert(0, @"insert prepare error"); } sqlite3_bind_text(statement, 1, [sapCustId UTF8String], -1, SQLITE_TRANSIENT); sqlite3_bind_text(statement, 2, [sapContactId UTF8String], -1, SQLITE_TRANSIENT); sqlite3_bind_text(statement, 3, [timestamp UTF8String], -1, SQLITE_TRANSIENT); sqlite3_bind_int(statement, 4, recordID); if (sqlite3_step(statement) != SQLITE_DONE) { NSLog(@"%s: insert step error: '%s'", __FUNCTION__, sqlite3_errmsg(databaseHandle)); sqlite3_finalize(statement); sqlite3_close(databaseHandle); NSAssert(0, @"insert step error"); } sqlite3_finalize(statement); sqlite3_close(databaseHandle); } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload