Viewing sqlite3_stmt* in command window - objective-c

I have inherited a SQLite database that is poorly commented and I need to understand what it is querying and receiving. Is there a way to see the value a sqlite3_stmt pointer is pointing to? Is it something that can be printed to the console in a printf() or NSLog()?
Thanks

The best way to do it is to trace the compiled statement. I've found a solution with the help of this question:
The sqlite3_trace command triggers a callback, which you need to code yourself. Once the trace is turned on it will execute for the rest of the program.
I've got all my database access routines in a single class, and I've used this as the callback function (it's a C function, not a method).
void traceCallback( void* udp, const char* sql )
{
printf("{SQL} [%s]\n", sql);
}
I turned the trace on in an init database method: it's turned on once the database is opened.
-(void)initializeDatabase
{
NSString *path = [self createEditableCopyOfDatabaseIfNeeded];
// open the db
if (sqlite3_open([path UTF8String], &db) == SQLITE_OK)
sqlite3_trace(db, traceCallback, NULL);
else {
// error - cleanup
sqlite3_close(db);
NSLog(#"Error opening db");
NSLog(#"Path: %#",path);
}
}
This will turn a SQL statement with bound variables into a string.
From this:
const char *sql2 = "select a.key, b.key from words a left outer join known_words b on a.key = b.word_id where a.word_foreign = :word_foreign";
To this:
{SQL} [select a.key, b.key from words a left outer join known_words b on a.key = b.word_id where a.word_foreign = 'bak.']

Related

NSObject description and custom summaries in Xcode

I override object's -(NSString*)description however Xcode always displays error: summary string parsing error in summary field in variables view.
My current implementation is the following:
- (NSString*)description {
return [NSString stringWithFormat:#"<%# %p> x=%f, y=%f", self.class, self, _x, _y];
}
If I type po objectName in console, LLDB shows a fine output as expected, however Xcode and command p objectName always indicate error, so what's the proper debug description format to make summary field work? Worth to notice that the output of "p" command is the same as a summary message that you see in Xcode for instances of Foundation classes.
Update:
As far as I can see from "WWDC 2012 session Debugging in Xcode", custom summaries can be implemented using Custom python script only. -(NSString*)description or -(NSString*)debugDescription methods are not connected anyhow to summary messages. I thought they are because I got an error displayed, but it seems it's a standard message for classes that do not have their own formatters.
I would suggest at least:
- (NSString*)description {
return [NSString stringWithFormat:#"%#; x=%f, y=%f", [super description], _x, _y];
}
So that you're not manually replicating the NSObject default and thereby blocking any non-default behaviour your superclass may have opted to include.
Beyond that, "summary string parsing error" is an lldb error. It's being reported by the debugger only. Per its documentation, po is correct for Objective-C objects; p is for C or C++ objects. So you needn't heed that error — it's essentially just telling you that you used the wrong lldb command.
EDIT: for what it's worth, the method used by CFArray is open source and looks like:
static CFStringRef __CFArrayCopyDescription(CFTypeRef cf) {
CFArrayRef array = (CFArrayRef)cf;
CFMutableStringRef result;
const CFArrayCallBacks *cb;
CFAllocatorRef allocator;
CFIndex idx, cnt;
cnt = __CFArrayGetCount(array);
allocator = CFGetAllocator(array);
result = CFStringCreateMutable(allocator, 0);
switch (__CFArrayGetType(array)) {
case __kCFArrayImmutable:
CFStringAppendFormat(result, NULL, CFSTR("<CFArray %p [%p]>{type = immutable, count = %u, values = (%s"), cf, allocator, cnt, cnt ? "\n" : "");
break;
case __kCFArrayDeque:
CFStringAppendFormat(result, NULL, CFSTR("<CFArray %p [%p]>{type = mutable-small, count = %u, values = (%s"), cf, allocator, cnt, cnt ? "\n" : "");
break;
}
cb = __CFArrayGetCallBacks(array);
for (idx = 0; idx < cnt; idx++) {
CFStringRef desc = NULL;
const void *val = __CFArrayGetBucketAtIndex(array, idx)->_item;
if (NULL != cb->copyDescription) {
desc = (CFStringRef)INVOKE_CALLBACK1(cb->copyDescription, val);
}
if (NULL != desc) {
CFStringAppendFormat(result, NULL, CFSTR("\t%u : %#\n"), idx, desc);
CFRelease(desc);
} else {
CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p>\n"), idx, val);
}
}
CFStringAppend(result, CFSTR(")}"));
return result;
}
As with the other comments above, I'm willing to gamble that the answer is: Xcode's debugger isn't smart in any sense and definitely isn't smart enough to use the correct po means of getting an Objective-C description; if your object is an uninflected Objective-C object then the debugger isn't going to be able to figure it out.

crash if CFDictionaryRef does not exist

i have an issue when disconnecting ethernet-cable from computer or just turned off ethernet. in this case some entrys do not exist and my app would crash.
so i tryed to find out how to prevent and just found CFDictionaryContainsKey, but this does not prevent the error. Anybody who knows an workaround which is also working lower than osx 10.6 ?
- (NSString *)checkNetworkInterface
{
SCDynamicStoreRef ds = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("myapp"), NULL, NULL);
CFDictionaryRef dr = SCDynamicStoreCopyValue(ds, CFSTR("State:/Network/Global/IPv4"));
Boolean ck = CFDictionaryContainsKey( dr, CFSTR("PrimaryInterface"));
NSString *interfaceString;
if (ck) {
CFStringRef interface = CFDictionaryGetValue(dr, CFSTR("PrimaryInterface"));
interfaceString = [NSString stringWithString:( NSString *)interface ];
} else {
interfaceString = [NSString stringWithString:#"" ];
}
CFRelease(dr);
CFRelease(ds);
return interfaceString;
}
if "State:/Network/Global/IPv4" does not exist, app crashes :(
As the documentation for SCDynamicStoreCopyValue() states:
Return Value: The value associated with the specified key, or NULL if no value was located or if an error occurred. You must release the returned value.
CFDictionaryContainsKey() attempts to inspect the passed-in dictionary; if it's NULL, you crash with a NULL pointer dereference. You also shouldn't CFRelease() a NULL pointer.
To correct this, just add a NULL check before calling CFDictionaryContainsKey().
NSString *interfaceString;
if(dr != NULL && CFDictionaryContainsKey(dr, CFSTR("PrimaryInterface")))
{
CFStringRef interface = CFDictionaryGetValue(dr, CFSTR("PrimaryInterface"));
...
CFRelease(dr);
}
I think you just want to check whether dr == NULL and abort if so. Apologies if this is a little obvious, but you're not doing it here and it seems like it would ward off the crash.

Print / Log the SQLite Statement after the binding

I like to log the compiled Statement after this:
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
sqlite3_bind_int( compiledStatement, 1, updateThis.web_id);
[...]
}
NSLog(#"Put out the complete SQLite Statement.");
The direct output failed, and i think this is not the way to do it:
NSLog(#"%#",compiledStatement);
You can't print the compiledStatement. What you can do is implement the sqlite3_trace callback function. This will print every executed sql statement.
To implement the function add this before your #implementation block
void sqliteCallbackFunc(void *foo, const char* statement) {
NSLog(#"=> %s", statement);
}
This is the function you will point to.
To point to this function simple call:
sqlite3_trace(db, sqliteCallbackFunc, NULL);

Delete column from Sqlite database

Basically I have to enter all textfield values in database then have to use for sending to webservice. So when the one column details send to service then have to delete that column.
I have done this:
-(void)deleteTableDataFromSavedDataTable:(NSString *)lastID {
NSString *sql_str=[NSString stringWithFormat:#"DELETE FROM FormInfoValues where Phone = %#",lastID];
const char *sql = [sql_str UTF8String];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSLog(#"sql delete statement is ");
sqlite3_stmt *deleteStmt;
if(sqlite3_prepare_v2(database, sql, -1, &deleteStmt, NULL) == SQLITE_OK)
{
NSLog(#"sql delete statement is %#", deleteStmt);
if(sqlite3_step(deleteStmt) != SQLITE_DONE )
{
NSLog( #"Error: %s", sqlite3_errmsg(database) );
}
else
{
NSLog( #"row id = %lld", (sqlite3_last_insert_rowid(database)+1));
NSLog(#"No Error");
}
}
sqlite3_finalize(deleteStmt);
}
sqlite3_close(database);
}
but its not deleting after sending. Why its not calling?
give me ideas..
It's quite possible I'm not understanding your issue properly -- so I apologize in advance if so.
Regarding: "...then have to delete that column."
-- If you want to clear the data in a column (by setting it to zero, or empty string, or NULL) then you'll want to use the UPDATE command in SQL. The DELETE command always deletes ROWS.
-- If you really must remove a column from a table's schema, you'll have to create a new table in sqlite. (Sqlite allows you to ADD a column via ALTER TABLE command, and some other databases DO allow you to drop a column using ALTER TABLE as well.) You can quickly copy a table (without it's constraints, etc) via e.g.:
CREATE TABLE MyOutput as SELECT a,b,d,f,h,z from MyOriginal;
-- If your output is created by a SELECT statement, just avoid using "*" and specify just the columns you want included.

QSqlQuery prepared statements - proper usage

I'm trying to determine the proper way to use prepared statements with QSqlQuery. The docs are not very specific on this subject.
void select(const QSqlDatabase &database) {
QSqlQuery query(database);
query.prepare("SELECT * FROM theUniverse WHERE planet = :planet");
query.bindValue(":planet", "earth");
query.exec();
}
So will this snippet create a permanent prepared statement in the connection database? Will this prepared statement persist between calls to select(), i.e. will it be saved when the function returns and QSqlQuery query is disposed?
Or should I create QSqlQuery on the heap and use the same instance over and over again?
Ok, the idea is that you have to create QSqlQuery on heap, prepare the query and do the following with it:
QSqlQuery::bindValue(s)
QSqlQuery::exec
read data with QSqlQuery::[next|first|last|...]
QSqlQuery::finish
rinse and repeat
the following snipped is useful to create, prepare and retrieve queries on heap:
QSqlDatabase database;
QMap<QString, QSqlQuery *> queries; //dont forget to delete them later!
QSqlQuery *prepareQuery(const QString &query)
{
QSqlQuery *ret = 0;
if (!queries.contains(query)) {
QSqlQuery *q = new QSqlQuery(database);
q->prepare(query);
queries[query] = ret = q;
} else {
ret = queries[query];
}
}
return ret;
}