Objective-C and MySQL - mysql

I've been able to connect to a MySQL database in my app and use the C API which is almost exactly like PHP commands (mysql_real_connect(), mysql_query(), mysql_fetch_array(), etc.) and which I am pretty comfortable with I'm just not sure how the data query is returned. Do I use an array or dictionary and then how would I parse it. For example, in PHP I would do something like so (after the connection):
$results = mysql_query("SELECT * FROM theDatabase");
if (mysql_num_rows($results) > 0) {
while($row = mysql_fetch_array($results)) {
print $row;
}
}
What would the objective-c equivalent be? Thanks.
Edit:
OK, so I made some progress - I can make the query and get the number of fields/rows returned, just can't seem to access the data itself. Here's my code, which I stitched together from the MySQL docs and a few other sites:
- (IBAction)dbConnect:(id)sender {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
MYSQL mysql;
mysql_init(&mysql);
if (!mysql_real_connect(&mysql, "10.1.1.99", "******", "******", "oldphotoarchive", 0, NULL, 0)) {
NSLog(#"%#", [NSString stringWithUTF8String:mysql_error(&mysql)]);
} else {
MYSQL_RES *result;
MYSQL_ROW row;
unsigned int num_fields;
unsigned int num_rows;
unsigned long *lengths;
if (mysql_query(&mysql,"SELECT * FROM photorecord")) {
// error
} else { // query succeeded, process any data returned by it
result = mysql_store_result(&mysql);
if (result) {
num_fields = mysql_num_fields(result);
while ((row = mysql_fetch_row(result))) {
lengths = mysql_fetch_lengths(result);
for(int i = 0; i < num_fields; i++) {
//the line below is my problem, printing row[i] fails, I get the GNU gdb error...
row[i] ? NSLog(#"%#", row[i]) : NSLog(#"wtf");
}
}
} else {// mysql_store_result() returned nothing; should it have?
if (mysql_errno(&mysql)) {
NSLog(# "Error: %s\n", mysql_error(&mysql));
} else if (mysql_field_count(&mysql) == 0) {
// query does not return data
// (it was not a SELECT)
num_rows = mysql_affected_rows(&mysql);
}
}
}
}
[pool release];
}

There's no Apple-supplied Objective-C API for MySQL. There are a few third-party wrappers of the C API, though. Take a look at the MySQL-Cocoa Framework, for example.
Given your familiarity with the PHP and C API, it may be more straightforward for you simply to use the C API. You'll need to handle conversion between objects and C data types, but this isn't much work.
Edit
You're crashing because the row value returned by the mysql API isn't an object, and your format string is telling NSLog to treat it as one. The %# is a format-string placeholder for an object, not a C data type.
It's not clear what the value is in this case. The context seems to imply that it's image data. If that's the case, you'll likely want to create an NSData object from the blob returned by the query, e.g.:
NSData *imageData;
imageData = [[ NSData alloc ] initWithBytes: row[ i ] length: lengths[ i ]];
NSLog( #"imageData: %#", imageData );
/* ...create NSImage, CGImage, etc... */
[ imageData release ];
If your result fields are just strings, use NSString's -initWithBytes:length:encoding: method:
NSString *s;
s = [[ NSString alloc ] initWithBytes: row[ i ] length: lengths[ i ]
encoding: NSUTF8StringEncoding ];
NSLog( #"result column %d: %#", i, s );
[ s release ];

Related

API call with invalid database connection pointer & misuse error when updating values in table - objective c application

I am working on a messaging iOS application and below is the code to update a table when a new message is received for a chat:
- (BOOL) updateInfoForChat:(Chat*)chat {
BOOL success;
int code = 0;
if (!dbOpen && dbConnection != NULL) {
if (sqlite3_open_v2([[DatabaseManager databasePath] UTF8String], &dbConnection, SQLITE_OPEN_READWRITE|SQLITE_OPEN_FULLMUTEX, NULL) == SQLITE_OK) {
dbOpen = YES;
const char* key = [[self getDBKey] UTF8String];
sqlite3_key(dbConnection, key, (int)strlen(key));
chat.lastMessage = [chat.lastMessage stringByReplacingOccurrencesOfString:#"\"" withString:#"%27"];
char *errMsg;
NSString *insertSQL = [NSString stringWithFormat:#"UPDATE Chats SET "
"lastMessageTime = %f, lastMessageText = \"%#\", canReply = \"%#\", isArchived = \"%#\" WHERE chatRoomId = \"%#\" AND lastMessageTime <= (SELECT messageTime FROM Messages WHERE chatRoomId = \"%#\" ORDER BY messageTime DESC LIMIT 1)",
chat.lastMsgTime, chat.lastMessage , chat.canReply ? #"True" : #"False" , chat.isArchived ? #"True" : #"False", chat.chatRoomID, chat.chatRoomID];
const char *sql_stmt = [insertSQL UTF8String];
code = (sqlite3_exec(dbConnection, sql_stmt, NULL, NULL, &errMsg));
if (code == SQLITE_OK) {
//commenting this out as this makes the logs very noisy
}
else {
NSLog(#"This method did not get executed with error: %s",sqlite3_errmsg(dbConnection));
}
sqlite3_close_v2(dbConnection);
dbOpen = NO;
}
else {
NSLog(#"This method did not get executed with error: %s",sqlite3_errmsg(dbConnection));
}
}
return success = (code == SQLITE_OK);
}
Having received messages in a couple of chats, I get the errors below when application hits this code:
API call with invalid database connection pointer
misuse at line 133563 of [d24547a13b]
This method did not get executed with error: bad parameter or other API misuse
Can someone suggest where the issue could be coming from?

MySQL command to delete a row in C is not working

I don't usually code using C programming language but I learned it in school (so please bear with me because I am still a newbie).
In short, I was recently assigned to write code in C in order to delete rows from a table in MySQL database.
I used stackoverflow and other resources to help me with this code!
This is my code (not all of it):
void delete_rows(MYSQL *con)
{
char selection_query[256];
char deletion_query[256];
sprintf(selection_query, "SELECT id FROM <table> WHERE status = 'PROCESSING'\
AND started < DATE(NOW()) - INTERVAL %d DAY", expire_processing_days);
if (mysql_query(con, selection_query))
{
finish_with_error(con);
}
MYSQL_RES *result = mysql_store_result(con);
if (result == NULL)
{
finish_with_error(con);
}
int num_fields = mysql_num_fields(result);
MYSQL_ROW row;
while ((row = mysql_fetch_row(result)))
{
for(int i = 0; i < num_fields; i++)
{
printf("Deleting process with id: %s ", row[i] ? row[i] : "NULL");
sprintf(deletion_query, "DELETE FROM <table> WHERE id = %d", row[i]);
if (mysql_query(con, deletion_query))
{
finish_with_error(con);
}
mysql_commit(con);
}
printf("\n");
}
mysql_free_result(result);
}
int main()
{
MYSQL *con;
DB_CONN_PARAMS *params = calloc(1,sizeof(DB_CONN_PARAMS));
//just an alternative way of passing connection params, find a struct easier
strcpy(params->host, <host>);
strcpy(params->user, <user>);
strcpy(params->pass, <password>);
strcpy(params->db, <database>);
MYSQL * connect_db(DB_CONN_PARAMS *params);
con = connect_db(params);
//we don't need the struct anymore
free(params);
params = NULL;
//kill processes that are incomplete/hanging
delete_rows(con);
//close mysql connection
mysql_close(con);
return EXIT_SUCCESS;
}
So, the code above compiles and runs without any errors, it prints out the ids of the rows that I want to delete. But when I go to the database to check the rows, they are still there!
Is there anything I am missing?
Ok, I have figured it out finally!
I changed the %d to %s in the following line:
sprintf(deletion_query, "DELETE FROM WHERE id = %d", row[i]);.
Because row[i] is a string, I was blind to that.
I was able to figure it out by printing the whole MySQL command and noticed that the id passed is wrong!
Thank you everyone for your attempts to help me.

Recursive function to parse JSON data from NSArray - Not returning correct number of results

I'm attempting to parse category data from a web store served in JSON Array format via REST into core data on iOS. Before I begin inserting to core data, i'm simply just logging the output to screen and keeping count of the results to check everything is ok.
The Problem In my test data set I have 152 categories, however I am only getting a 'Final counter' of 141 outputted to the log?
I've looked and looked at the recursive function and believe its ok, therefor I think the problem lies somewhere in the findSubcategoriesForCategoryID function?
Any feedback on the problem would be most grateful as this has kept me up for hours now.
Example JSON data returned from Web Service:
Node: {
categoryID = 259;
categoryTitle = "Engine Parts";
parentID = 0; // Parent ID of 0 indicates a root category
}
Node: {
categoryID = 300;
categoryTitle = "Camshafts";
parentID = 259; // Parent ID indicates this category is a subcategory
}
Node: {
categoryID = 317;
categoryTitle = "Kent Camshafts";
parentID = 300;
}
The following methods are what I have so far in my application.
/**
* Kickstarts parsing operation
*/
- (void)parseCategoriesData:(NSArray *)downloadedData {
NSMutableDictionary *fakeCategory = [NSMutableDictionary dictionary];
[fakeCategory setObject:[NSNumber numberWithInt:0] forKey:#"categoryID"];
int counter = 0;
[self recursiveFunction:downloadedData parentCategory:fakeCategory counter:&counter];
NSLog(#"Final counter = %d", counter);
}
/**
* Recursive function to traverse the passed NSArray
*/
- (void)recursiveFunction:(NSArray *)array parentCategory:(id)parentCategory counter:(int *)i {
NSArray *subCategories = [self findSubcategoriesForCategoryID:[[parentCategory valueForKey:#"categoryID"] intValue] categoryData:array];
for (id object in subCategories) {
NSLog(#"Node: %# depth: %d",[object description], *i);
*i = *i + 1;
[self recursiveFunction:array parentCategory:object counter:i];
}
}
/**
* Returns an NSArray of subcategories for the passed categoryID
*/
- (NSArray *)findSubcategoriesForCategoryID:(int)categoryID categoryData:(NSArray *)categoryData {
NSIndexSet *indexsForFilteredCategories = [categoryData indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return (BOOL)([[obj valueForKey:#"parentID"] intValue] == categoryID);
}];
return [categoryData objectsAtIndexes:indexsForFilteredCategories];
}
Your recursive function looks OK, but it is rather convoluted, so only a test can guarantee that it really works for all special cases.
If I understand your algorithm correctly, you start from the top and go down to the items that have the present id as the parent id. It follows, that you might have parent ids that do not exist as category ids.
This is quite easy to test:
NSArray *allIDs = [downloadedData objectForKey:#"categoryID"];
NSArray *allParentIDs = [downloadedData objectForKey:#"parentID"];
for (NSNumber *x in allParentIDs) {
if (x.intValue==0) continue;
NSArray *allChildren = [allIDs filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:#"self = %#", x]];
if (allChildren.count == 0) {
NSLog(#"There are no category ids for parent id %d.", x.intValue);
}
}

Printing records alternative way

Is there a way to directly display the content of a query in Mysql using C?
What I mean is:
through mysql shell if I type : SELECT * FROM table_name; I get the query result in a neat and formatted way.
If I want to do the same thing using Api C I have to write several lines of codes and the final result is far from being nice (at least this is my personal experience )
For example :
void display_Table1(MYSQL *conn)
{
int jj,ii;
char query[512];
sprintf(query, "SELECT * FROM Table1 ;");
if (mysql_query (conn, query)) {
printf("\nErrore query:\n");
printf("%s", mysql_error(conn),"\n");
result = mysql_store_result(conn);
if (result) {
num_rows = mysql_num_rows(result);
num_fields =mysql_num_fields(result);
//printf("Number of rows=%u Number of fields=%d \n", num_rows,num_fields);
//printf(" ");
}
else
{
printf("Result set is empty");
}
// Print column headers
fields = mysql_fetch_fields(result);
for(jj=0; jj < num_fields; jj++)
{
printf("\n%s\t\t",fields[jj].name);
}
printf("\n\t ");
// print query results
while(row = mysql_fetch_row(result)) // row pointer in the result set
{
for(ii=0; ii < num_fields; ii++)
{
printf("%s\t", row[ii] ? row[ii] : "NULL"); // Not NULL then print
}
printf("\n");
}
if(result)
{
mysql_free_result(result);
result = NULL;
}
}
}
That's a knotty problem to solve. I get headers one after the other in a vertical way.
I also get
Commands out of sync; you can't run this command now
Firstly, there is no direct way to print out a formatted display. What you can do, is use
MYSQL_FIELD *field = mysql_fetch_field (resultset);
col_len = field->max_length;
if(col_len < strlen(field->name))
col_len = strlen(field->name);
to find out the maximum width of a column, and the space the data accordingly.

how to get RAW Data when i use AudioQueue to Record voice?

when i use AudioQueue to Record voice to file, this is ok.
i try at MyInputBufferHandler function use
AudioQueueBufferRef->mAudioData
can get raw data, but in this MyInputBufferHandler function
can't call other object , like oStream .
i want get AudioQueue Buffer's raw data , and send this raw data to internet ,how to do ?
You need to set the format the way you want to receive data to AudioQueue, refer following function,
http://developer.apple.com/library/mac/#documentation/MusicAudio/Reference/CoreAudioDataTypesRef/Reference/reference.html
One example,
FillOutASBDForLPCM (sRecordFormat,
16000,
1,
8,
8,
false,
false
);
See the answer to this question, which gives you raw data. You can then bundle it as NSData or whatever, zip and upload.
You need to modify some codes in myInputBufferHandler, I had created a obj-c object to adopt the cpp code from Apple SpeakHere sample.
Please feel free to use it:
MIP_StreamAudioRecorder.h
//
// MIP_StreamAudioRecorder.h
//
// Created by Dennies Chang on 12/10/3.
// Copyright (c) 2012年 Dennies Chang. All rights reserved.
//
#import <Foundation/Foundation.h>
#include <AudioToolbox/AudioToolbox.h>
#include <Foundation/Foundation.h>
#include <libkern/OSAtomic.h>
#include "CAStreamBasicDescription.h"
#include "CAXException.h"
#define kNumberRecordBuffers 3
#protocol MIP_StreamAudioRecorderDelegate;
#interface MIP_StreamAudioRecorder : NSObject {
CAStreamBasicDescription mRecordFormat;
AudioQueueRef mQueue;
AudioQueueBufferRef mBuffers[kNumberRecordBuffers];
BOOL mIsRunning;
id <MIP_StreamAudioRecorderDelegate> delegate;
}
#property (nonatomic, assign) id <MIP_StreamAudioRecorderDelegate> delegate;
#property (nonatomic, readonly) BOOL mIsRunning;
- (void)SetupAudioFormat:(UInt32) inFormatID;
- (void)startRecord;
- (void)stopRecord;
- (int)computeRecordBufferSize:(AudioStreamBasicDescription *)format duration:(float)second;
#end
#protocol MIP_StreamAudioRecorderDelegate <NSObject>
#optional
- (void)gotAudioData:(NSData *)audioData;
#end
And .mm file : MIP_StreamAudioRecorder.mm
//
// MIP_StreamAudioRecorder.mm
//
// Created by Dennies Chang on 12/10/3.
// Copyright (c) 2012年 Dennies Chang. All rights reserved.
//
#import "MIP_StreamAudioRecorder.h"
#implementation MIP_StreamAudioRecorder
#synthesize delegate;
#synthesize mIsRunning;
- (id)init {
self = [super init];
return self;
}
- (void)dealloc {
[super dealloc];
}
- (void)SetupAudioFormat:(UInt32) inFormatID {
memset(&mRecordFormat, 0, sizeof(mRecordFormat));
UInt32 size = sizeof(mRecordFormat.mSampleRate);
XThrowIfError(AudioSessionGetProperty( kAudioSessionProperty_CurrentHardwareSampleRate,
&size,
&mRecordFormat.mSampleRate), "couldn't get hardware sample rate");
size = sizeof(mRecordFormat.mChannelsPerFrame);
XThrowIfError(AudioSessionGetProperty( kAudioSessionProperty_CurrentHardwareInputNumberChannels,
&size,
&mRecordFormat.mChannelsPerFrame), "couldn't get input channel count");
mRecordFormat.mFormatID = inFormatID;
if (inFormatID == kAudioFormatLinearPCM)
{
// if we want pcm, default to signed 16-bit little-endian
mRecordFormat.mChannelsPerFrame = 1;
mRecordFormat.mSampleRate = 8000;
mRecordFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
mRecordFormat.mBitsPerChannel = 16;
mRecordFormat.mBytesPerPacket = mRecordFormat.mBytesPerFrame = (mRecordFormat.mBitsPerChannel / 8) * mRecordFormat.mChannelsPerFrame;
mRecordFormat.mFramesPerPacket = 1;
}
}
- (int)computeRecordBufferSize:(AudioStreamBasicDescription *)format duration:(float)second {
int packets, frames, bytes = 0;
try {
frames = (int)ceil(second * format->mSampleRate);
if (format->mBytesPerFrame > 0)
bytes = frames * format->mBytesPerFrame;
else {
UInt32 maxPacketSize;
if (format->mBytesPerPacket > 0)
maxPacketSize = format->mBytesPerPacket; // constant packet size
else {
UInt32 propertySize = sizeof(maxPacketSize);
XThrowIfError(AudioQueueGetProperty(mQueue, kAudioQueueProperty_MaximumOutputPacketSize, &maxPacketSize,
&propertySize), "couldn't get queue's maximum output packet size");
}
if (format->mFramesPerPacket > 0)
packets = frames / format->mFramesPerPacket;
else
packets = frames; // worst-case scenario: 1 frame in a packet
if (packets == 0) // sanity check
packets = 1;
bytes = packets * maxPacketSize;
}
} catch (CAXException e) {
char buf[256];
fprintf(stderr, "Error: %s (%s)\n", e.mOperation, e.FormatError(buf));
return 0;
}
return bytes;
}
/*
- (void)myInputBufferHandler:(id)inUserData AudioQueue:(AudioQueueRef) inAQ BufferRef:(AudioQueueBufferRef)inBuffer withAudioTS:(AudioTimeStamp *)inStartTime andNumPackets:(UInt32)inNumPackets andDescription:(AudioStreamPacketDescription *)inPacketDesc {
*/
void MyInputBufferHandler( void * inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer,
const AudioTimeStamp * inStartTime,
UInt32 inNumPackets,
const AudioStreamPacketDescription* inPacketDesc)
{
MIP_StreamAudioRecorder *THIS = (MIP_StreamAudioRecorder *)inUserData;
try {
if (inNumPackets > 0) {
//use delegate to handle;
if (THIS.delegate) {
NSMutableData *data = [[NSMutableData alloc] init];
if ([THIS.delegate respondsToSelector:#selector(gotAudioData:)]) {
[data appendBytes:inBuffer->mAudioData length:inBuffer->mAudioDataByteSize];
[THIS.delegate gotAudioData:data];
}
[data release];
}
/*
// write packets to file
XThrowIfError(AudioFileWritePackets(aqr->mRecordFile, FALSE, inBuffer->mAudioDataByteSize,
inPacketDesc, aqr->mRecordPacket, &inNumPackets, inBuffer->mAudioData),
"AudioFileWritePackets failed");
aqr->mRecordPacket += inNumPackets;
*/
}
// if we're not stopping, re-enqueue the buffe so that it gets filled again
if (THIS->mIsRunning)
XThrowIfError(AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL), "AudioQueueEnqueueBuffer failed");
} catch (CAXException e) {
char buf[256];
fprintf(stderr, "Error: %s (%s)\n", e.mOperation, e.FormatError(buf));
}
}
- (void)startRecord {
int i, bufferByteSize;
try {
[self SetupAudioFormat:kAudioFormatLinearPCM];
// create the queue
XThrowIfError(AudioQueueNewInput(
&mRecordFormat,
MyInputBufferHandler,
self /* userData */,
NULL /* run loop */, NULL /* run loop mode */,
0 /* flags */, &mQueue), "AudioQueueNewInput failed");
// get the record format back from the queue's audio converter --
// the file may require a more specific stream description than was necessary to create the encoder.
UInt32 size = sizeof(mRecordFormat);
XThrowIfError(AudioQueueGetProperty(mQueue, kAudioQueueProperty_StreamDescription,
&mRecordFormat, &size), "couldn't get queue's format");
// allocate and enqueue buffers
bufferByteSize = [self computeRecordBufferSize:&mRecordFormat duration:kBufferDurationSeconds]; // enough bytes for half a second
for (i = 0; i < kNumberRecordBuffers; ++i) {
XThrowIfError(AudioQueueAllocateBuffer(mQueue, bufferByteSize, &mBuffers[i]),
"AudioQueueAllocateBuffer failed");
XThrowIfError(AudioQueueEnqueueBuffer(mQueue, mBuffers[i], 0, NULL),
"AudioQueueEnqueueBuffer failed");
}
// start the queue
mIsRunning = true;
XThrowIfError(AudioQueueStart(mQueue, NULL), "AudioQueueStart failed");
}
catch (CAXException &e) {
char buf[256];
fprintf(stderr, "Error: %s (%s)\n", e.mOperation, e.FormatError(buf));
}
catch (...) {
fprintf(stderr, "An unknown error occurred\n");
}
}
- (void)stopRecord {
XThrowIfError(AudioQueueStop(mQueue, true), "AudioQueueStop failed");
AudioQueueDispose(mQueue, true);
}
#end
Please get informed, you should change the sampleRate and relative condition,
I set it as mono (1 channel), 16 bit, 8Khz to record.
And you can get the raw data in the obj-c code which implement MIP_StreamAudioRecorderDelegate, you can send the raw data with internet channel,
or save it to file.
Best Regard,
Dennies.