Open ecanas opened 12 years ago
This is the function,
else if(type == SQLITE_BLOB){
//NSLog(@"blob");
NSData *aData = [[NSData alloc]dataWithBytes:sqlite3_column_blob(statement, colNum) length:sqlite3_column_bytes(statement,colNum)];
[row addObject:aData];
[aData autorelease];
}
else{//SQLITE_NULL
//NSLog(@"column type: %d",type);
[row addObject:@"null"];
}
case SQLITE_BLOB: columnValue = [NSString stringWithUTF8String:(char *)sqlite3_column_blob(statement, i)]; columnName = [NSString stringWithFormat:@"%s", sqlite3_column_name(statement, i)]; [entry setObject:columnValue forKey:columnName];
break;
yip, missing something to do for null and blob
switch (column_type) {
case SQLITE_INTEGER:
columnValue = [NSNumber numberWithDouble: sqlite3_column_double(statement, i)];
columnName = [NSString stringWithFormat:@"%s", sqlite3_column_name(statement, i)];
[entry setObject:columnValue forKey:columnName];
break;
case SQLITE_TEXT:
columnValue = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, i)];
columnName = [NSString stringWithFormat:@"%s", sqlite3_column_name(statement, i)];
[entry setObject:columnValue forKey:columnName];
break;
case SQLITE_BLOB:
break;
case SQLITE_FLOAT:
columnValue = [NSNumber numberWithFloat: sqlite3_column_double(statement, i)];
columnName = [NSString stringWithFormat:@"%s", sqlite3_column_name(statement, i)];
[entry setObject:columnValue forKey:columnName];
break;
case SQLITE_NULL:
break;
}
i++;
ns-1m comments might work ...
The problem is also when entering data it will not enter as null. Was able add the option for case SQLITE_NULL and SQLITE_BLOB as per my code above, however that's only part of the problem since data is not saved as null.
This is a fix I came up with:
https://github.com/marcucio/Phonegap-SQLitePlugin/commit/8660ba16fd796ad764e0c8a1eda906c47dbf8b57
maybe it fixes your issue
Looks like will work.. will give it a go. Can you also update the project and add the corresponding code for the case SQLITE_BLOB and SQLITE_NULL.. I updated my version with the following..
case SQLITE_ROW: i = 0; entry = [NSMutableDictionary dictionaryWithCapacity:0]; count = sqlite3_column_count(statement); NSMutableArray *row = [[NSMutableArray alloc] initWithCapacity:count];
while (i < count) {
column_type = sqlite3_column_type(statement, i);
switch (column_type) {
case SQLITE_INTEGER:
//columnValue = [NSNumber numberWithInt: sqlite3_column_int(statement, i)];
//columnName = [NSString stringWithFormat:@"%s", sqlite3_column_name(statement, i)];
//[entry setObject:columnValue forKey:columnName];
[row addObject:[NSNumber numberWithInt: sqlite3_column_int(statement, i)]];
break;
case SQLITE_TEXT:
//columnValue = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, i)];
//columnName = [NSString stringWithFormat:@"%s", sqlite3_column_name(statement, i)];
//[entry setObject:columnValue forKey:columnName];
[row addObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, i)]];
break;
case SQLITE_BLOB:
//NSData *aData = [[NSData alloc]dataWithBytes:sqlite3_column_blob(statement, i) length:sqlite3_column_bytes(statement,i)];
[row addObject:[NSData dataWithBytes:sqlite3_column_blob(statement, i) length:sqlite3_column_bytes(statement,i)]];
break;
case SQLITE_FLOAT:
//columnValue = [NSNumber numberWithFloat: sqlite3_column_double(statement, i)];
//columnName = [NSString stringWithFormat:@"%s", sqlite3_column_name(statement, i)];
//[entry setObject:columnValue forKey:columnName];
[row addObject:[NSNumber numberWithFloat: sqlite3_column_double(statement, i)]];
break;
//case SQLITE_NULL:
default:
[row addObject:@"null"];
break;
}
i++;
}
//[resultRows addObject:entry];
[resultRows addObject:row];
[row release];
row = nil;
break;
@ecanas your code seems not to work for me, please post a diff or the entire file, maybe i missed something
Here is the complete code, with the latest change you made. Will see if tomorrow I can post just the difference.. but the only changes are on the line "NSMutableArray *row = [[NSMutableArray alloc] initWithCapacity:count];"
/*
@implementation PGSQLitePlugin
@synthesize openDBs; @synthesize appDocsPath;
-(PGPlugin) initWithWebView:(UIWebView)theWebView { self = (PGSQLitePlugin*)[super initWithWebView:theWebView]; if (self) { openDBs = [NSMutableDictionary dictionaryWithCapacity:0]; [openDBs retain];
PGFile* pgFile = [[self appDelegate] getCommandInstance: @"com.phonegap.file"];
NSString *docs = [pgFile appDocsPath];
[self setAppDocsPath:docs];
}
return self;
}
-(void) respond: (id)cb withString:(NSString )str withType:(NSString *)type { if (cb != NULL) { NSString jsString = [NSString stringWithFormat:@"PGSQLitePlugin.handleCallback('%@', '%@', %@);", cb, type, str ]; [self writeJavascript:jsString]; } }
-(id) getDBPath:(id)dbFile { if (dbFile == NULL) { return NULL; } NSString *dbPath = [NSString stringWithFormat:@"%@/%@", appDocsPath, dbFile]; return dbPath; }
-(void) open: (NSMutableArray)arguments withDict:(NSMutableDictionary)options { NSString callback = [options objectForKey:@"callback"]; NSString dbPath = [self getDBPath:[options objectForKey:@"path"]];
if (dbPath == NULL) {
[self respond:callback withString:@"{ message: 'You must specify database path' }" withType:@"error"];
return;
}
sqlite3 *db;
const char *path = [dbPath UTF8String];
if (sqlite3_open(path, &db) != SQLITE_OK) {
[self respond:callback withString:@"{ message: 'Unable to open DB' }" withType:@"error"];
return;
}
NSValue *dbPointer = [NSValue valueWithPointer:db];
[openDBs setObject:dbPointer forKey: dbPath];
[self respond:callback withString: @"{ message: 'Database opened' }" withType:@"success"];
}
-(void) backgroundExecuteSqlBatch: (NSMutableArray)arguments withDict:(NSMutableDictionary)options { [self performSelector:@selector(_executeSqlBatch:) withObject:options afterDelay:0.001]; }
-(void) backgroundExecuteSql: (NSMutableArray)arguments withDict:(NSMutableDictionary)options { [self performSelector:@selector(_executeSql:) withObject:options afterDelay:0.001]; }
-(void) _executeSqlBatch:(NSMutableDictionary*)options { [self executeSqlBatch:NULL withDict:options]; }
-(void) _executeSql:(NSMutableDictionary*)options { [self executeSql:NULL withDict:options]; }
-(void) executeSqlBatch: (NSMutableArray)arguments withDict:(NSMutableDictionary)options { NSMutableArray executes = [options objectForKey:@"executes"]; for (NSMutableDictionary dict in executes) { [self executeSql:NULL withDict:dict]; } }
-(void) executeSql: (NSMutableArray)arguments withDict:(NSMutableDictionary)options { NSString callback = [options objectForKey:@"callback"]; NSString dbPath = [self getDBPath:[options objectForKey:@"path"]]; NSMutableArray query_parts = [options objectForKey:@"query"]; NSString query = [query_parts objectAtIndex:0];
if (dbPath == NULL) {
[self respond:callback withString:@"{ message: 'You must specify database path' }" withType:@"error"];
return;
}
if (query == NULL) {
[self respond:callback withString:@"{ message: 'You must specify a query to execute' }" withType:@"error"];
return;
}
NSValue *dbPointer = [openDBs objectForKey:dbPath];
if (dbPointer == NULL) {
[self respond:callback withString:@"{ message: 'No such database, you must open it first' }" withType:@"error"];
return;
}
sqlite3 *db = [dbPointer pointerValue];
const char *sql_stmt = [query UTF8String];
char *errMsg = NULL;
sqlite3_stmt *statement;
int result, i, column_type, count;
int previousRowsAffected, nowRowsAffected, diffRowsAffected;
long long previousInsertId, nowInsertId;
BOOL keepGoing = YES;
BOOL hasInsertId;
NSMutableDictionary *resultSet = [NSMutableDictionary dictionaryWithCapacity:0];
NSMutableArray *resultRows = [NSMutableArray arrayWithCapacity:0];
NSMutableDictionary *entry;
//NSObject *columnValue;
//NSString *columnName;
NSString *bindval;
NSObject *insertId;
NSObject *rowsAffected;
hasInsertId = NO;
previousRowsAffected = sqlite3_total_changes(db);
previousInsertId = sqlite3_last_insert_rowid(db);
if (sqlite3_prepare_v2(db, sql_stmt, -1, &statement, NULL) != SQLITE_OK) {
errMsg = (char *) sqlite3_errmsg (db);
keepGoing = NO;
} else {
for (int b = 1; b < query_parts.count; b++) {
bindval = [NSString stringWithFormat:@"%@", [query_parts objectAtIndex:b]];
if([bindval isEqualToString:@"<null>"])
bindval = NULL;
sqlite3_bind_text(statement, b, [bindval UTF8String], -1, SQLITE_TRANSIENT);
}
}
while (keepGoing) {
result = sqlite3_step (statement);
switch (result) {
case SQLITE_ROW:
i = 0;
entry = [NSMutableDictionary dictionaryWithCapacity:0];
count = sqlite3_column_count(statement);
NSMutableArray *row = [[NSMutableArray alloc] initWithCapacity:count];
while (i < count) {
column_type = sqlite3_column_type(statement, i);
switch (column_type) {
case SQLITE_INTEGER:
[row addObject:[NSNumber numberWithInt: sqlite3_column_int(statement, i)]];
break;
case SQLITE_TEXT:
[row addObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, i)]];
break;
case SQLITE_BLOB:
[row addObject:[NSData dataWithBytes:sqlite3_column_blob(statement, i) length:sqlite3_column_bytes(statement,i)]];
break;
case SQLITE_FLOAT:
[row addObject:[NSNumber numberWithFloat: sqlite3_column_double(statement, i)]];
break;
case SQLITE_NULL:
[row addObject:@"null"];
break;
default:
[row addObject:@""];
break;
}
i++;
}
[resultRows addObject:row];
[row release];
row = nil;
break;
case SQLITE_DONE:
nowRowsAffected = sqlite3_total_changes(db);
diffRowsAffected = nowRowsAffected - previousRowsAffected;
rowsAffected = [NSNumber numberWithInt:diffRowsAffected];
nowInsertId = sqlite3_last_insert_rowid(db);
if (previousInsertId != nowInsertId) {
hasInsertId = YES;
insertId = [NSNumber numberWithLongLong:sqlite3_last_insert_rowid(db)];
}
keepGoing = NO;
break;
default:
errMsg = "SQL statement error";
keepGoing = NO;
}
}
sqlite3_finalize (statement);
if (errMsg != NULL) {
[self respond:callback withString:[NSString stringWithFormat:@"{ message: 'SQL statement error : %s' }", errMsg] withType:@"error"];
} else {
[resultSet setObject:resultRows forKey:@"rows"];
[resultSet setObject:rowsAffected forKey:@"rowsAffected"];
if (hasInsertId) {
[resultSet setObject:insertId forKey:@"insertId"];
}
[self respond:callback withString:[resultSet JSONRepresentation] withType:@"success"];
}
}
-(void) close: (NSMutableArray)arguments withDict:(NSMutableDictionary)options { NSString callback = [options objectForKey:@"callback"]; NSString dbPath = [self getDBPath:[options objectForKey:@"path"]]; if (dbPath == NULL) { [self respond:callback withString:@"{ message: 'You must specify database path' }" withType:@"error"]; return; }
NSValue *val = [openDBs objectForKey:dbPath];
sqlite3 *db = [val pointerValue];
if (db == NULL) {
[self respond:callback withString: @"{ message: 'Specified db was not open' }" withType:@"error"];
}
sqlite3_close (db);
[self respond:callback withString: @"{ message: 'db closed' }" withType:@"success"];
}
-(void)dealloc { int i; NSArray keys = [openDBs allKeys]; NSValue pointer; NSString key; sqlite3 db;
/* close db the user forgot */
for (i=0; i<[keys count]; i++) {
key = [keys objectAtIndex:i];
pointer = [openDBs objectForKey:key];
db = [pointer pointerValue];
sqlite3_close (db);
}
[openDBs release];
[appDocsPath release];
[super dealloc];
}
@end
I try to use this patch above Cordova 1.5, app is crashing.
marcucio, I made a few other changes now it's 100% working with nulls. Where can I post the 2 updated files.
If you are set up for github you can add it to your account if not you can use gist:
Ok..used gist.. here is the link https://gist.github.com/2340433
It seems that null values are either not being saved or returned properly. Instead of saving a null value in a field I get 0 or '' depending on the datatype.
Below is code from QuickConnect which has a native client, maybe you can use some of the code, I noticed they have a blob section which you can use on your code and they have a function called "sqlite3_bind_null" not sure what it's doing exactly but could fix the null value problem.
/* Copyright (c) 2008, 2009 Lee Barney Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product was created using the QuickConnect framework. http://www.quickconnectfamily.org", in the same place and form as other third-party acknowledgments. Alternately, this acknowledgment may appear in the software itself, in the same form and location as other such third-party acknowledgments.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import "SQLiteDataAccess.h"
import "QCParameter.h"
import
static NSString *dbSemaphore = @"dblock";
// Private interface for AppDelegate - internal only methods. @interface SQLiteDataAccess (Private)
@end
@implementation SQLiteDataAccess
(SQLiteDataAccess)initWithDatabase: (NSString) dbName isWriteable: (BOOL) isWriteable{ //if (self = [super init]) { NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:dbName]; if(isWriteable){
} sqlite3 *aDatabase; //NSLog(@"path: %@",path); if (sqlite3_open([path UTF8String], &aDatabase) == SQLITE_OK) { //NSLog(@"database opened"); self->database = aDatabase;
} else{ //since we failed to open the database completely close it down to make sure that everyting is cleaned up sqlite3_close(aDatabase); NSAssert1(0, @"Failed to open database with message '%s'.", sqlite3_errmsg(aDatabase)); } //} return nil; }
(DataAccessResult)dbAccess:(NSString)SQL withParameters:(NSArray_)parameters treatAsChangeData:(BOOL)treatAsChangeData{ SQL = [SQL stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; //NSLog(@"paramArrayString after 1: %@\n\n\n",SQL); //NSLog(@"in dbAccess"); DataAccessResult theResult; //what is being returned theResult = [DataAccessResult alloc]; if(parameters != nil && [parameters count] > 0){ //make sure the the number of parameters is equal to the number of qestion marks in the SQL string } int numResultColumns = 0; sqlite3_stmt statement = nil; // Preparing a statement compiles the SQL query into a byte-code program in the SQLite library. // The third parameter is either the length of the SQL string or -1 to read up to the first null terminator. const char_ SQLChar = [SQL UTF8String];//this needs to be deallocated after we don't need it any more. //NSLog(@"about to prepare %@",SQL); if (sqlite3_prepare_v2(database, SQLChar, -1, &statement, NULL) == SQLITE_OK) { if(!treatAsChangeData){ //NSLog(@"columns not changing"); //retrieve the number of columns in the result of the execution of the select statement numResultColumns = sqlite3_column_count(statement); //NSLog(@"numRecentColumns: %i",numResultColumns); NSAutoreleasePool pool = [[NSAutoreleasePool alloc] init]; NSMutableArray fieldNames = [[NSMutableArray alloc] initWithCapacity:0]; for(int i = 0; i < numResultColumns; i++){ const char name = sqlite3_column_name(statement, i); NSString \ columnName = [[NSString alloc]initWithCString:name encoding:NSUTF8StringEncoding]; [fieldNames addObject:columnName]; [columnName autorelease]; } [theResult setFieldNames:fieldNames]; [fieldNames autorelease];
} else{ /NSString error; error = [[NSString alloc]initWithCString:sqlite3_errmsg(database) encoding:NSASCIIStringEncoding]; [theResult setErrorDescription:error]; [error release]; */ [theResult setErrorDescription:[NSString stringWithUTF8String:sqlite3_errmsg(database)]];
} // "Finalize" the statement - releases the resources associated with the statement. sqlite3_finalize(statement); //[pool drain]; //NSLog(@"returning: %@",theResult); return theResult; }
}
// internal bind methods
(int) bind_blob:(sqlite3stmt)statement withIndex:(int)parameterIndex andBindVariable:(NSData_)aVariable{
if (![aVariable respondsToSelector:@selector(lengthOfBytes:)]) { return -1; } //by default have the library make a copy, SQLITE_TRANSIENT, since we don't know if the variable may be changed //by something else in the application. return sqlite3_bind_blob(statement, parameterIndex, aVariable, [aVariable length], SQLITE_TRANSIENT); }
@end