Mysql using in Cocoa - mysql

Dear community. The code bellow compiled fine, start, but return empty array. Same code with same base return well. May somebody suggest something?
-(NSArray *) mysqlDirectQuery:(NSString *)query
{
// query doesn't using now
NSMutableArray *rows = [[NSMutableArray alloc] initWithCapacity:0];
MYSQL *sql = mysql_init( NULL );
//mysql_options(sql, MYSQL_READ_DEFAULT_GROUP, "LibMySQL");
sql = mysql_real_connect(sql, "localhost", "root", "", "test2", 3306, NULL, 0);
if (sql != NULL) {
mysql_query(sql, "select * from Users");
MYSQL_RES *qResult = mysql_store_result(sql);
if (qResult != NULL) {
MYSQL_ROW row;
while (row == mysql_fetch_row(qResult)) {
[rows addObject:[NSArray arrayWithObjects:
[NSString stringWithCString:row[0] encoding:NSISOLatin1StringEncoding],
nil]];
}
mysql_free_result(qResult);
}
mysql_close(sql);
mysql_server_end();
}
NSArray *resultArray = [NSArray arrayWithArray:rows];
rows = nil;
return resultArray;
}

Shouldn't this:
while (row == mysql_fetch_row(qResult))
be
while (row = mysql_fetch_row(qResult))
You are using row[0] below, but you are not assigning it.

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?

C++/CLI & mySQL: How to form queries from user input/choice

This is for homework, so I understand if you don't want to just give me the answer. That being said, if you do decide to "give" me anything, please explain what it does, and why, and how, because, to be perfectly honest, I'm in an online program and the teacher/class style is very unhelpful. I.E. When we ask for help, we just get a link to the MSDN main page and are told to figure it out. (PS: if anyone ever considers going to 'City University' for a MS in CS: don't).
I've been working on this 1 problem for over 30 hours now, and searched high and low on this website and many others, and have been coming up with not much to show for it. Any help, explanations, tips, etc, would be greatly appreciated.
The assignment is to take user input in a C++/CLI console app, connect it to a mySQL database via ODBC32, and run a loop asking for user choice about which table to pull up and display. I have done the loop, I have gotten the db connected (I think/hope), but I can't seem to convert the users choice into a valid SQL query, or maybe it's just a display issue. Since I am quite literally teaching myself, I have to admit that I don't really know if this is where the true root of the problem lies, or if I have grossly misunderstood what this code is and does.
My current code:
#include "stdafx.h"
#include <string>
#include <sql.h>
#include <sqlext.h>
#pragma comment( lib, "odbc32.lib" )
#define EMPLOYEE_ID_LEN 32
SQLHENV henv = NULL;
SQLHDBC hdbc = NULL;
SQLRETURN retcode;
SQLHSTMT hstmt = NULL;
SQLSMALLINT sCustID;
SQLCHAR szEmployeeID[EMPLOYEE_ID_LEN];
SQL_DATE_STRUCT dsOrderDate;
SQLINTEGER cbCustID = 0, cbOrderDate = 0, cbEmployeeID = SQL_NTS;
int main()
{
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER*)SQL_OV_ODBC3, 0);
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
retcode = SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
retcode = SQLConnect(hdbc, (SQLWCHAR*) "MySqlWorld", SQL_NTS, (SQLWCHAR*)NULL, 0, NULL, 0);
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
bool boolExit = false;
while (boolExit == false)
{
String^ strChoice = "";
do {
if (strChoice == "a")
{
strChoice = "city";
break;
}
else if (strChoice == "b")
{
strChoice = "country";
break;
}
else if (strChoice == "c")
{
strChoice = "countrylanguage";
break;
}
else if (strChoice == "e")
{
boolExit = true;
break;
}
else
{
Console::WriteLine();
Console::WriteLine("Please choose from the following options:");
Console::WriteLine("A: Enter last name to retreive first name, home address, and work phone number.");
Console::WriteLine("B: Enter last name to retrieve department.");
Console::WriteLine("C: Enter department to retrieve list of employee last names, addresses, and work phone numbers for members of that department.");
Console::WriteLine("E: Exit");
strChoice = Console::ReadLine()->ToLower();
}
} while (true);
if (boolExit == false)
{
char* cstrTempQuery = (char*)(void*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(strChoice);
SQLCHAR sqlTempQuery[32];
strcpy_s((char*)sqlTempQuery, _countof(sqlTempQuery), (const char *)cstrTempQuery);
retcode = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, EMPLOYEE_ID_LEN, 0, sqlTempQuery, 0, &cbEmployeeID);
retcode = SQLPrepare(hstmt, (SQLWCHAR*)"SELECT * from ?", SQL_NTS);
retcode = SQLExecute(hstmt);
}
} // end while (boolExit == false)
} // end main()
The code results in no errors of any sort, but doesn't do anything. My loop shows, I make a selection, and nothing happens, except that the loop repeats.
I got most of this code from this page: https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlbindparameter-function
You cannot bind tablename or field name parameters. You can dynamically construct the query using using curly braces. It is not advised but possible with this:
https://stackoverflow.com/a/11327831/5389997
Something similar to : SELECT * FROM {$mytable}
Since you're specifying your tables that you need then you don't have to worry so much about the SQL injection angle.
It also looks like you're not updating the boilerplate code to have your variables.
#include "stdafx.h"
#include <string>
#include <sql.h>
#include <sqlext.h>
#pragma comment( lib, "odbc32.lib" )
#define QUERY_LEN 32
SQLHENV henv = NULL;
SQLHDBC hdbc = NULL;
SQLRETURN retcode;
SQLHSTMT hstmt = NULL;
SQLCHAR szQueryVar[QUERY_LEN];
SQLCHAR queryVar = SQL_NTS;
int main()
{
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER*)SQL_OV_ODBC3, 0);
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
retcode = SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
retcode = SQLConnect(hdbc, (SQLWCHAR*) "MySqlWorld", SQL_NTS, (SQLWCHAR*)NULL, 0, NULL, 0);
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
bool boolExit = false;
while (boolExit == false)
{
String^ strChoice = "";
String^ tableName = "";
String^ queryField = "";
String^ queryVar = "";
do {
if (strChoice == "a")
{
tableName = "address";
queryField = "last_name";
queryVar = Console::ReadLine();
break;
}
else if (strChoice == "b")
{
tableName = "department";
queryField = "last_name";
queryVar = Console::ReadLine();
break;
}
else if (strChoice == "c")
{
tableName = "department";
queryField = "department";
queryVar = Console::ReadLine();
break;
}
else if (strChoice == "e")
{
boolExit = true;
break;
}
else
{
Console::WriteLine();
Console::WriteLine("Please choose from the following options:");
Console::WriteLine("A: Enter last name to retreive first name, home address, and work phone number.");
Console::WriteLine("B: Enter last name to retrieve department.");
Console::WriteLine("C: Enter department to retrieve list of employee last names, addresses, and work phone numbers for members of that department.");
Console::WriteLine("E: Exit");
strChoice = Console::ReadLine()->ToLower();
}
} while (true);
if (boolExit == false)
{
char* cstrTempQuery = (char*)(void*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(strChoice);
SQLCHAR sqlTempQuery[32];
strcpy_s((char*)sqlTempQuery, _countof(sqlTempQuery), (const char *)cstrTempQuery);
retcode = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, QUERY_LEN, 0, sqlTempQuery, 0, &queryVar);
query = (SQLCHAR*)"SELECT * from {$tableName} where {$queryField} = ?";
retcode = SQLPrepare(hstmt, queryVar, SQL_NTS);
retcode = SQLExecute(hstmt);
}
} // end while (boolExit == false)
} // end main()

how to make key/value pairing for NSDictionary

my Dictionary output is-
i want to fire insert command to insert this dictionary data to the sqlite database
(
{
iCardReceiverName = rp;
iCardSenderName = madhusudan;
isDeleted = 0;
isReceiverSynk = 0;
},
{
iCardReceiverName = rp;
iCardSenderName = mmmm;
isDeleted = 0;
isReceiverSynk = 0;
}
)
i want to insert this data into sqlite database.
here is my code-
NSDictionary *jsonData = [NSJSONSerialization
JSONObjectWithData:urlData
options:NSJSONReadingMutableContainers
error:&error];
NSMutableString *query = [NSMutableString stringWithFormat:#"Insert into iCardUsers ("];
[self.dbManager executeQuery:query];
if (self.dbManager.affectedRows != 0) {
NSLog(#"Query was executed successfully. Affected rows = %d", self.dbManager.affectedRows);
}
else{
NSLog(#"Could not execute the query.");
}
for (int i = 0; i<[[jsonData allKeys] count]; i++)
{
[query appendFormat:#"%#,",[[jsonData allKeys] objectAtIndex:i]];
}
[query appendFormat:#")values ("];
for (int i = 0; i<[[jsonData allKeys] count]; i++)
{
[query appendFormat:#"%#,",[jsonData valueForKey:[[jsonData allKeys] objectAtIndex:i]]];
}
[query appendFormat:#");"];
NSLog(#"qry : %#",query);
You can insert your data in sqlite db like in following example.
You can create a function with required parameters and insert data using the function.
-(void)InsertRecords:(NSMutableString *)txt{
NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:#"movieData.sqlite"];
const char *dbpath = [dbPath UTF8String];
sqlite3 *contactDB;
sqlite3_stmt *statement;
NSLog(#"%#",dbPath);
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO myMovies (movieName) VALUES (\"%#\")", txt];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(contactDB, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
sqlite3_bind_text(statement, 1, [txt UTF8String], -1, SQLITE_TRANSIENT);
} else {
NSLog(#"error");
}
sqlite3_finalize(statement);
sqlite3_close(contactDB);
}
}

Converting a MySQL result into a JSON string in C

How do I convert a result from a MySQL query to a JSON string in C?
Of course I know how to do it, I just wondered if there's already a solution to copy-paste, realizing I don't want to write boiler-plate code.
And how do I Google for this question? Google just disregard the c and shows results for PHP etc.
The C file I made to do this is four times faster than the corresponding PHP file, using ab to measure performance:
ab -k -c 300 -n 10000 localhost/tiny.php
Time per request: 393.072 [ms] (mean)
With C:
ab -k -c 300 -n 10000 localhost/cgi/tiny.fcgi
Time per request: 98.237 [ms] (mean)
This is assuming Apache spawns 10 processes of tiny.fcgi, and PHP does not use FastCGI.
FastCgiServer /var/www/cgi/tiny.fcgi -processes 10
This is the PHP code, which connects to MySQL, fetch a query result and echo the JSON representation:
<?php
$mysqli = mysqli_connect("localhost", "user", "password", "db");
mysqli_set_charset($mysqli, "utf8");
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$result = mysqli_query($mysqli, "SELECT * FROM table");
$rows = array();
while ($row = mysqli_fetch_assoc($result)) {
$rows[] = $row;
}
echo json_encode($rows);
Let's step through the C code. I will use the PHP internal struct smart_str to handle strings (this is what PHP use in the json_encode function). Using the naive char* for strings will be catastrophical, because the number of malloc and free will be huge due to string append usage.
We want to use FastCGI:
#include "fcgi_stdio.h"
int main(void)
{
while(FCGI_Accept() >= 0)
{
}
FCGI_Finish();
return 0;
}
Analysing this with valgrind gives 768 bytes still reachable at end, which we will ignore (bug in FastCGI probably).
Next comes MySQL connection and query:
MYSQL* connection = NULL;
MYSQL_RES* result = NULL;
connection = mysql_init(NULL);
if (connection == NULL)
{
(void) FCGI_fprintf(FCGI_stderr, "Could not connect to MySQL: %s\n", mysql_error(connection));
continue;
}
// Connect to database
if (mysql_real_connect(connection, "localhost", "user", "password", "db", 0, NULL, 0) == NULL)
{
close_mysql_with_error(connection);
continue;
}
// Select from pages
if (mysql_query(connection, "SELECT * FROM table") != 0)
{
close_mysql_with_error(connection);
continue;
}
// Get result
result = mysql_store_result(connection);
// Abort if no result
if (result == NULL)
{
close_mysql_with_error(connection);
continue;
}
(I use continue instead of exit or return, because this code is within the while loop seen above.)
Nothing strange here, right?
The next part will create our smart_str JSON variable, pass it to function result_to_json and then echo the result.
smart_str json = {0, 0, 0};
result_to_json(result, &json);
if (json.c != NULL)
(void) FCGI_printf("json = %s\n", json.c);
smart_str_free(&json);
The result_to_json is just a loop over the rows in the MySQL result:
static void result_to_json(MYSQL_RES *result, smart_str* json)
{
MYSQL_ROW row;
int i;
int num_fields = (int) mysql_num_fields(result);
smart_str** fields = get_field_names(result, num_fields);
if (fields == NULL)
{
return;
}
smart_str_appendc(json, '[');
while ((row = mysql_fetch_row(result, num_fields)))
{
smart_str_appendl(json, "{", 1);
for (i = 0; i < num_fields; i++)
{
// key
smart_str_appendl(json, "\"", 1);
smart_str_appendl(json, fields[i]->c, fields[i]->len);
smart_str_appendl(json, "\": ", 3);
if (row[i] == NULL)
{
smart_str_appendl(json, "null", 4);
smart_str_appendl(json, ", ", 2);
}
else
{
smart_str_appendl(json, "\"", 1);
smart_str_appendl(json, row[i], strlen(row[i]));
smart_str_appendl(json, "\", ", 3);
}
}
if (json == NULL) {
free_field_names(fields, num_fields);
return;
}
// Strip last ','
json->len--;
json->len--;
smart_str_appendl(json, "}, ", 3);
}
if (json == NULL)
{
free_field_names(fields, num_fields);
return;
}
// Strip last ','
json->len--;
json->len--;
smart_str_appendl(json, "]", 1);
smart_str_0(json);
free_field_names(fields, num_fields);
return;
}
Finally, get_field_names and free_field_names:
static smart_str** get_field_names(MYSQL_RES *my_result, int num_fields)
{
smart_str** fields; // Array of pointers
MYSQL_FIELD *field = NULL;
int i;
// Allocate size of array
fields = malloc(num_fields * sizeof(smart_str*));
if (fields == NULL)
{
return NULL;
}
for (i = 0; i < num_fields; i++)
{
field = mysql_fetch_field(my_result);
if (field == NULL) {
// TODO: Free fields[]
free(fields);
return NULL;
}
fields[i] = malloc(sizeof(smart_str));
if (fields[i] == NULL) {
// TODO: Free fields[]
free(fields);
return NULL;
}
else
{
fields[i]->c = NULL;
smart_str_appendl(fields[i], field->name, strlen(field->name));
}
return fields;
}
static void free_field_names(smart_str** strings, int size)
{
int i;
for (i = 0; i < size; i++)
{
smart_str_free(strings[i]);
free(strings[i]);
}
free(strings);
}
There you go! What's left to do is to measure against FastCGI enabled PHP, to see how much the PHP performance improves.

Objective-C and 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 ];