query sqlite error out of memory xcode - objective-c

I have an error. Problem with prepare statement: out of memory.
How to fix it?
Other queries are working normally. I do not know what can be.
Maybe this is a problem -(TelefonDetail *)telefonDetails:(int)iDMob ??
-(TelefonDetail *)telefonDetails:(int)iDMob
{
TelefonDetail *retvalTelefon = nil;
NSString *ZaprosTelefons = #"SELECT iDMob , marka, model,wifi, os, razmeri,Display,Camera,Stoimos,imageTel,opisCrat FROM MobTele WHERE iDMob=1";
sqlite3_stmt *statement1;
if (sqlite3_prepare_v2(_database, [ZaprosTelefons UTF8String], -1, &statement1, nil)
!= SQLITE_OK) {
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(_database));
}
else{ while (sqlite3_step(statement1) == SQLITE_ROW) {
int iDMob = sqlite3_column_int(statement1, 0);
char *Marka = (char *) sqlite3_column_text(statement1, 1);
char *Model = (char *) sqlite3_column_text(statement1, 2);
char *Wifi = (char *) sqlite3_column_text(statement1, 3);
char *OS = (char *) sqlite3_column_text(statement1, 4);
char *Razmeri = (char *) sqlite3_column_text(statement1, 5);
char *Display = (char *) sqlite3_column_text(statement1, 6);
char *Camera = (char *) sqlite3_column_text(statement1, 7);
char *Stoimos = (char *) sqlite3_column_text(statement1, 8);
Byte *imgTel = (Byte *) sqlite3_column_blob(statement1, 9);
NSString *marka = [[NSString alloc] initWithUTF8String:Marka];
NSString *model = [[NSString alloc] initWithUTF8String:Model];
NSString *wifi = [[NSString alloc] initWithUTF8String:Wifi];
NSString *os = [[NSString alloc] initWithUTF8String:OS];
NSString *razmeri = [[NSString alloc] initWithUTF8String:Razmeri];
NSString *display = [[NSString alloc] initWithUTF8String:Display];
NSString *camera = [[NSString alloc] initWithUTF8String:Camera];
NSString *Stoimost = [[NSString alloc] initWithUTF8String:Stoimos];
int len = sqlite3_column_bytes(statement1, 9);
NSData *imgData = [[NSData alloc] initWithBytes:imgTel length:len];
retvalTelefon = [[TelefonDetail alloc]initWhithIDMob:iDMob marka:marka model:model wifi:wifi os:os razmeri:razmeri display:display camera:camera Stoimost:Stoimost imegeTel:imgData];
}
sqlite3_finalize(statement1);
}
return retvalTelefon;
}

The "out of memory" error is often a misleading error message caused by trying to use a database without having opened it first (or if you accidentally set the sqlite3 database pointer to NULL). For example:
sqlite3 *db = NULL;
sqlite3_stmt *statement;
int rc;
// deliberately did not open database -- ERROR
// now try to use SQLite without opening database
if ((rc = sqlite3_prepare_v2(db, "select * from test", -1, &statement, NULL)) != SQLITE_OK)
NSLog(#"rc=%d errmsg=%s", rc, sqlite3_errmsg(db));
This will generate a return code (rc) of 21, SQLITE_MISUSE. And the error message is a misleading "out of memory":
2014-05-11 17:36:22.035 MyApp[19942:60b] rc=21 errmsg=out of memory

Related

Save and retrieve AttributedString to SQLite3

I have a project in Objective-c in which I am trying to find a way of saving the attributedText from a UITextView to a SQLite3 table.
My Project Target OS is 12.1.
I am using an object called "MMItem" with a NSData property called "notesAttributed".
In my viewController Class I am using NSKeyedArchiver to encode the AttributedText into a NSdata format then copying to the object property.
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.itemNotes.attributedText requiringSecureCoding:YES error:Nil];
self.item.notesAttributed = data;
I then call a method in my model to save the object
NSString *resultStr = [self.meetingModel saveAttributedItemNote:item];
In the model I'm attempting to save the attributed string to a field in the Item table setup as type 'blob'
- (NSString *)saveAttributedItemNote:(MMItem *)item{
NSString *errorMessage;
NSString *sql;
NSInteger result = 0;
if (item) {
//create blob encoded data for Attributed notes
NSInteger itemID = item.itemID;
sql = [NSString stringWithFormat:
#"UPDATE Item set noteAttributed = %# WHERE itemID = %ld",item.notesAttributed, (long)itemID];
char *err;
// open DB and save
if ([self openDB]){
//NSLog(#"%#", NSStringFromSelector(_cmd));
result = sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err);
sqlite3_close(db);
}
if (result != SQLITE_OK)
{
errorMessage = #"Error";
}
else
{
//NSLog(#"Table updated");
errorMessage = #"OK";
[self saveAttributedItemNote:item];
}
}
item = nil;
return errorMessage;
}
The SQL Execute statement fails with error 1.
I suspect I'm meant to insert the blob into the table using 'binding' and not an Execute statement but I just find how to put this all together using Objective-c.
Any help would be great thanks.
With acknowledgement to the following post iOS SQLite Blob data is saving NULL
I managed to get a working solution. It's apparent that to save a blob into an SQLite you must use a Prepare statement with a sqlite3_bind_blob to insert the blob parameters into the statement, then to use sqlite3_step to deploy it.
This then allows the bytes and length parameters to also be passed into the statement, which I don't think can be done using the execute method I was originally trying.
Here is the code that works perfectly.
- (NSString *)saveAttributedItemNote:(MMItem *)item{
NSString *errorMessage;
if (item) {
//create blob encoded data for Attributed notes
int itemID = (int)item.itemID;
if ([self openDB]) {
const char *insert_stmt = "UPDATE Item set notesAttributed = ? WHERE itemID = ?";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(db, insert_stmt, -1, &statement, NULL) == SQLITE_OK) {
sqlite3_bind_blob(statement, 1, item.notesAttributed.bytes, (int)item.notesAttributed.length, SQLITE_TRANSIENT);
sqlite3_bind_int(statement, 2, itemID);
if (sqlite3_step(statement) == SQLITE_DONE) {
errorMessage = #"OK";
//update successfull
}else{
const char *err = sqlite3_errmsg(db);
NSString *error = [[NSString alloc] initWithUTF8String:err];
NSLog(#"Update error ID:%#",error);
errorMessage = #"Error";
}
sqlite3_finalize(statement);
}else{
errorMessage = #"Error";
NSLog(#"Unable to prepare stsement %s: %s",insert_stmt, sqlite3_errmsg(db));
}
sqlite3_close(db);
}
}
item = nil;
return errorMessage;
}
The following code then shows how the field was retrieved from sqlite. Column 11 is the blob.
if (sqlite3_prepare(db, [sql UTF8String], -1, &statement, nil)==SQLITE_OK)
{
//NSLog(#"SQL Select OK");
while (sqlite3_step(statement)==SQLITE_ROW) {
MMItem *item = [[MMItem alloc]init];
item.itemID = sqlite3_column_int(statement, 0);
item.topicID = sqlite3_column_int(statement, 1);
item.sequenceID = sqlite3_column_int(statement, 2);
char *description = (char *) sqlite3_column_text(statement, 3);
if(description) item.itemDescription = [[NSString alloc]initWithUTF8String:description];
char *notes = (char *) sqlite3_column_text(statement, 4);
if(notes) item.notes = [[NSString alloc]initWithUTF8String:notes];
char *actionBy = (char *) sqlite3_column_text(statement, 5);
if(actionBy) item.actionBy = [[NSString alloc]initWithUTF8String:actionBy];
char *requiredBy = (char *) sqlite3_column_text(statement, 6);
if (requiredBy) item.requiredBy = [[NSString alloc]initWithUTF8String:requiredBy];
item.completed = sqlite3_column_int(statement, 7);
char *proposedBy = (char *) sqlite3_column_text(statement, 8);
if (proposedBy) item.proposedBy = [[NSString alloc]initWithUTF8String:proposedBy];
char *secondedBy = (char *) sqlite3_column_text(statement, 9);
if (secondedBy) item.secondedBy = [[NSString alloc]initWithUTF8String:secondedBy];
item.carried = sqlite3_column_int(statement, 10);
NSData *attributedTextData = [[NSData alloc]initWithBytes:sqlite3_column_blob(statement,11) length:sqlite3_column_bytes(statement, 11)];
item.notesAttributed = attributedTextData;
[items addObject:item];
item = nil;
}
}
Then in the ViewController, the following was used to take the NSData property and decode this for the UITextView (self.itemNotes)
self.itemNotes.attributedText = [NSKeyedUnarchiver unarchivedObjectOfClass:([NSAttributedString class]) fromData:self.item.notesAttributed error:&error];
Weird how just posting the question lead me to finding the right solution. Thanks to El Tomato for your help bud.

Xcode - NSInvalidArgumentException', reason: '*** +[NSString stringWithUTF8String:]: NULL cString'

I have the below code that should populate the TableViewController with information from my sqlite file, it lets me add fine, and i can view the file and the information is there, but I'm getting the above error message, and failing miserably at fixing it....
-(NSMutableArray *) stockList
{
NSString *filePath = [self getWritableDBPath];
if(sqlite3_open([filePath UTF8String], &db) == SQLITE_OK)
{
const char *sql = "Select Description, Quantity from StockTable";
sqlite3_stmt *sqlStatement;
if(sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK)
{
NSLog(#"Problem with query: %s", sqlite3_errmsg(db));
}
else
{
while (sqlite3_step(sqlStatement)==SQLITE_ROW)
{
Stock * stock = [[Stock alloc] init];
stock.desc = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 1)];
stock.qty = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 2)];
[thestock addObject:stock];
stock = nil;
}
}
sqlite3_finalize(sqlStatement);
}
sqlite3_close(db);
return thestock;
}
thanks for any help, currently googling it myself..
connection strings as mentioned below: (reason being it causes a LINK error and states that MyDB is a duplicate in both views)
TableView:
NSString * MyDB2=#"StockDatabase.db";
AddingView:
NSString * MyDB=#"StockDatabase.db";
One (or both) of the columns you are fetching from the database is NULL:
stock.desc = [NSString stringWithUTF8String:(char *)sqlite3_column_text(sqlStatement, 1)];
stock.qty = [NSString stringWithUTF8String:(char *)sqlite3_column_text(sqlStatement, 2)];
Guard against that with:
const char *desc = sqlite3_column_text(sqlStatement, 1);
if (desc)
stock.desk = #(desc);
const char *qty = sqlite3_column_text(sqlStatement, 2);
if (qty)
stock.qty = #(qty);
I got this error too. How I solved it is by setting the first column text index to "0" instead of "1". And the error went away.
char *charPrice = (char*) sqlite3_column_text(stmt, 0);
NSString *price = [NSString stringWithUTF8String:charPrice];
char *charName = (char*) sqlite3_column_text(stmt, 1);
NSString *name = [NSString stringWithUTF8String:charName];

sqlite3_prepare_v2 failing

Could anyone explain to me why this if statement is not triggering? The database opens just fine, it's just that I can't retrieve any values from the database. Also, the table name is correct as well.
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil)== SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
int uniqueId = sqlite3_column_int(statement, 0);
char *nameChars = (char *) sqlite3_column_text(statement, 1);
char *addressChars = (char *) sqlite3_column_text(statement, 2);
NSString *name = [[NSString alloc] initWithUTF8String:nameChars];
NSString *address = [[NSString alloc] initWithUTF8String:addressChars];
PersonInfo *info = [[PersonInfo alloc] initWithUniqueID:uniqueId name:name address:address];
[returnArray addObject:info];
}
sqlite3_finalize(statement);
}
return returnArray;
}

iOS - object sent autorelease too many times

I have a method that is reading some information from a sqlite db and initialising a class called Achievement. When I analyse this code I am given the feedback 'object sent autorelease too many times'. I don't really understand where I am going wrong - why is the retval object released on line 225 and not at the return statement on line 229?
Can someone please explain where I have made a mistake in the code below and how I can fix it?
Function Code (so answerer can easily copy/paste):
- (Achievement *)getAchievement:(int)Id
{
Achievement *retval = [[Achievement alloc] autorelease];
NSString *query = [NSString stringWithFormat:#"SELECT * FROM Achievements where ID = %d", Id];
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, nil)
== SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
int Id = sqlite3_column_int(statement, 0);
char *name = (char *) sqlite3_column_text(statement, 1);
char *title = (char *) sqlite3_column_text(statement, 2);
char *description = (char *) sqlite3_column_text(statement, 3);
Boolean Achieved;
char *com = (char *) sqlite3_column_text(statement, 4);
NSString *c1 = [[[NSString alloc] initWithUTF8String:com] autorelease];
Achieved = [c1 isEqualToString:#"1"];
NSDate *CompletedDate = (NSDate *) sqlite3_column_text(statement, 5);
char *icon = (char *) sqlite3_column_text(statement, 6);
int New = sqlite3_column_int(statement, 7);
NSString *Title = [[[NSString alloc] initWithUTF8String:title] autorelease];
NSString *Description = [[[NSString alloc] initWithUTF8String:description] autorelease];
NSString *Name = [[[NSString alloc] initWithUTF8String:name] autorelease];
NSString *Icon = [[[NSString alloc] initWithUTF8String:icon] autorelease];
retval = [retval initDetails:Id :Name :Title: Description : Achieved : CompletedDate: Icon: New];
}
sqlite3_finalize(statement);
}
return retval;
}
Analysis feedback image:
As always any feedback is greatly appreciated.
Achievement *retval = [[Achievement alloc] autorelease];
this is very bad idea to do that. You ALWAYS have to initialize object before using it.
Instead you're initializing it in a loop:
retval = [retval initDetails:Id :Name :Title: Description : Achieved : CompletedDate: Icon: New];
I don't really get why you need to initialize the same object multiple times. Maybe, you need to create multiple objects and init them with different values?
Rearrange this:
Achievement *retval = nil;
while (...) {
[retval release];
retval = [[Achievement alloc] initDetails: ...];
}
return [retval autorelease];
I guess you're confusing the compiler with the wrong sequence of alloc, init, autorelease. What you should be doing instead is the following (pseudocode):
Achievement *retval = nil;
while (...) {
retval = [[[Achievement alloc] initDetails: ...] autorelease];
}
return retval;

Comparing NSString to SQLite database field

I have these codes that retrieve value from the SQLite database and comparing it to a NSString declared in the header file.
Retrieving from database:
NSString *sql = [[NSString alloc] initWithFormat:#"SELECT TESTONE, TESTTWO, TESTTHREE, TESTFOUR FROM STUDENTS WHERE NAME='%#'",Name];
sqlite3_stmt *statement;
if(sqlite3_prepare_v2(db, [sql UTF8String], -1, &statement, nil)== SQLITE_OK)
{
while(sqlite3_step(statement) == SQLITE_ROW){
char *one = (char *) sqlite3_column_text(statement,0);
tOne = [[NSString alloc] initWithUTF8String:one];
char *two = (char *) sqlite3_column_text(statement,1);
tTwo = [[NSString alloc] initWithUTF8String:two];
char *three = (char *) sqlite3_column_text(statement,2);
tThree = [[NSString alloc] initWithUTF8String:three];
char *four = (char *) sqlite3_column_text(statement,3);
tFour = [[NSString alloc] initWithUTF8String:four];
}
}
Comparing string:
if(tOne == #"Yes")
{
//blablabla
}
else if(tOne == #"No")
{
//blablabla
}
It doesn't seem to go into the 'IF' at all.
I have double-checked the field in the database and its TEXT datatype.
I made a UILabel to display the tOne value and it shows "Yes".
May i know what or where went wrong during the comparison?
You can't compare strings like this, you have to use isEqualToString:
if([tOne isEqualToString:#"Yes"])
{
//blablabla
}
else if([tOne isEqualToString:#"No"])
{
//blablabla
}
You are currently comparing the address of the NSString object, not its contents. Use the compare: or isEqualToString: family of methods to compare the content of the string.
Reference.