C program cannot get result value from MySQL procedure - mysql

I am practicing with getting a value back from a MySQL stored procedure
So I first created the following procedure
USE testdb;
DROP PROCEDURE IF EXISTS `testdb`.`get_return_value_test`;
DELIMITER $$
CREATE PROCEDURE IF NOT EXISTS `testdb`.`get_return_value_test`(IN a INT(30), IN b INT, OUT result INT)
BEGIN
SET result = a+b;
SELECT result;
END $$
DELIMITER ;
and successfully tested it from MariaDB console
MariaDB [testdb]> call get_return_value_test(2, 3, #out_value);
+--------+
| result |
+--------+
| 5 |
+--------+
1 row in set (0.000 sec)
Query OK, 0 rows affected (0.000 sec)
But when I have to use it within a C program I don't get the result.
mysql_stmt_fetch function returns 101 value, which I have never seen in MySQL documentation
mysql_stmt_errno is 0
Do you know where I went wrong?
Thank you
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql/mysql.h>
#include "utils.c"
static MYSQL *conn;
static void get_return_value_test(MYSQL *conn)
{
int a = 3;
int b = 4;
int result = -1;
int error_number;
MYSQL_STMT *prepared_stmt;
MYSQL_BIND param[3];
// Prepare stored procedure call
if(!setup_prepared_stmt(&prepared_stmt, "call get_return_value_test(?, ?, #out_value)", conn))
{
printf("Unable to run setup_prepared_stmt\n");
mysql_stmt_close(prepared_stmt);
mysql_close(conn);
exit(EXIT_FAILURE);
}
// Prepare parameters
memset(param, 0, sizeof(param));
param[0].buffer_type = MYSQL_TYPE_LONG;
param[0].buffer = &a;
param[0].buffer_length = sizeof(a);
param[1].buffer_type = MYSQL_TYPE_LONG;
param[1].buffer = &b;
param[1].buffer_length = sizeof(b);
param[2].buffer_type = MYSQL_TYPE_LONG;
param[2].buffer = &result;
param[2].buffer_length = sizeof(result);
if (mysql_stmt_bind_param(prepared_stmt, param) != 0)
{
printf("Could not bind parameters\n");
mysql_stmt_close(prepared_stmt);
mysql_close(conn);
exit(EXIT_FAILURE);
}
// Run procedure
if ((error_number = mysql_stmt_execute(prepared_stmt)) != 0)
{
printf("%d", error_number);
printf("mysql_stmt_execute error.");
mysql_stmt_close(prepared_stmt);
mysql_close(conn);
exit(EXIT_FAILURE);
}
else
{
printf("mysql_stmt_execute correctly executed\n");
}
memset(param, 0, sizeof(param));
if((error_number = mysql_stmt_bind_result(prepared_stmt, param)) != 0)
{
printf("%d", error_number);
printf("Could not retrieve output parameter");
mysql_stmt_close(prepared_stmt);
mysql_close(conn);
exit(EXIT_FAILURE);
}
//FAILS HERE
if((error_number = mysql_stmt_fetch(prepared_stmt)) != 0 )
{
printf("%d\n", error_number);
printf("mysql_stmt_errno is %d\n", mysql_stmt_errno(prepared_stmt));
finish_with_stmt_error(conn, prepared_stmt, "Could not buffer results\n", true);
}
printf("Result is %d\n", result);
mysql_stmt_close(prepared_stmt);
}
int main()
{
conn = mysql_init (NULL);
if (conn == NULL)
{
fprintf (stderr, "mysql_init() failed (probably out of memory)\n");
exit(EXIT_FAILURE);
}
if (mysql_real_connect(conn, "localhost", "login", "login", "testdb", 3306, NULL, CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS) == NULL)
{
fprintf (stderr, "mysql_real_connect() failed\n");
printf(mysql_error(conn));
mysql_close (conn);
exit(EXIT_FAILURE);
}
get_return_value_test(conn);
mysql_close (conn);
return 0;
}
utils.h

101 is defined as the value for MYSQL_DATA_TRUNCATED in mysql.h
https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-fetch.html says:
MYSQL_DATA_TRUNCATED is returned when truncation reporting is enabled. To determine which column values were truncated when this value is returned, check the error members of the MYSQL_BIND structures used for fetching values. Truncation reporting is enabled by default, but can be controlled by calling mysql_options() with the MYSQL_REPORT_DATA_TRUNCATION option.
You may not have seen this before because you were using older versions of MySQL that did not enable this reporting option by default.
You probably should return the value from your function as a 64-bit integer, because the sum of two 32-bit integers may overflow.
Okay I am taking a closer look at your code, and I see you reuse the params array for the result binding as well as the parameter binding. You memset the params array to zeroes before binding it for results.
But I see in https://dev.mysql.com/doc/c-api/8.0/en/c-api-prepared-call-statements.html that the array used for results binding needs some values initialized, according to the result set metadata. It looks like your array is just going to be all zeroes since you did memset. I'm guessing that your buffer_length being zero is a problem which could result in the error you saw.
So I suggest reviewing the code example in the manual that shows getting results from the CALL statement, and do similar steps for initializing your result buffers.

Related

Store binary data structure into BLOB columns in C

I load a dictionary and made many manipulation in it. To save some CPU time, I actually store the result into a flat file for future use. How can I store that memory structure into a BLOB columns of MariaDB database (program in C)
He is my actual code et what an example of what I try to do.
//--- Global variables and CONST for databases
#define DB_NAME "some_name"
#define DB_USER "admin"
#define DB_PWD "qwerty"
#define DB_SERVER "localhost"
MYSQL *conn;
MYSQL_RES *res;
MYSQL_ROW row;
int main (int argc, char **argv)
{
char str_query [2048] ;
//----------------------------------------------------------------
// The way I dump the memory (structure) into a flat file actually
//----------------------------------------------------------------
fd = fopen("./mem_dump.binary","wb");
fwrite(&st_dic, sizeof(struct Dictionary), 1, fd);
fclose (fd);
//----------------------------------------------------------------
// The way I need to do it
// Insert the structure into a BLOB column
//----------------------------------------------------------------
conn = mysql_init(NULL);
if (!mysql_real_connect(conn, DB_SERVER, DB_USER, DB_PWD, DB_NAME, 0, NULL, 0))
{
fprintf(stderr, "ERROR:%s\n", mysql_error(conn));
exit (1) ;
}
//....
sprintf (str_query, "INSERT INTO myTables (id, blob_field) VALUES (0, '%s')", &st_dic) ;
//....
if (mysql_query(conn, str_query))
{
fprintf(stderr,"FAIL TO RUN SQL : [%s]\n", str_query) ;
fprintf(stderr, "%s\n", mysql_error(conn));
}
mysql_free_result(res);
mysql_close(conn);
return (0) ;
}
When working with binary objects text protocol (mysql_query/mysql_real_query) is not the best option, since special characters like '\0' are not supported. That means you have to allocate additional buffer (2 * (size of blob) + 1) for transforming the binary object.
Solution 1: mysql_real_escape()
char *buffer = malloc(sizeof(struct Dictionary) * 2 + 1);
mysql_real_escape_string(conn, buffer, &st_dic, sizeof(struct Dictionary));
sprintf(str, "INSERT INTO myTables (id, blob_field) VALUES (0, '%s')", buffer);
if (mysql_query(conn, str))
{
/* Error handling */
}
Solution 2: mysql_hex_string()
char *buffer = malloc(sizeof(struct Dictionary) * 2 + 1);
mysql_hex_string(buffer, &st_dic, sizeof(struct Dictionary));
sprintf(str, "INSERT INTO myTables (id, blob_field) VALUES (0, X'%s')", buffer);
if (mysql_query(conn, str))
{
/* Error handling */
}
Alternative:
A better solution is to use prepared statements which use the binary protocol:
Code without error handling:
MYSQL_BIND bind;
MYSQL_STMT *stmt;
stmt= mysql_stmt_init(conn);
mysql_stmt_prepare(stmt, INSERT INTO myTables (id, blob_field) VALUES (0, ?)", 1);
memset(&bind, 0, sizeof(MYSQL_BIND));
bind.buffer_type= MYSQL_TYPE_BLOB;
bind.buffer= &st_dic;
bind.buffer_length= sizeof(struct Dictionary);
mysql_stmt_bind_param(stmt, &bind);
mysql_stmt_execute(stmt);

How to store a value from a MySQL database in a C variable

I'm starting with C and MySQL, I have created a database with some columns and I want to store two of them and use it in other parts of the code. In the example that I give i'm trying to store one value of "latitud" from de database in variable float lat, but it give me an error in the core. Do you know how can I solve this? Thank you.
#include <stdio.h>
#include <stdlib.h>
#include <mysql/mysql.h>
#define HOST "localhost"
#define USER "victor"
#define PASS "tfg"
#define DATABASE "barcos"
int main (int argc, char *argv[]){
MYSQL *con;
MYSQL_ROW row;
MYSQL_RES *res;
con = mysql_init(NULL);
if (!mysql_real_connect(con, HOST, USER, PASS, DATABASE, 0, NULL, 0)){ // CONEXIÓN A LA BASE DE DATOS
fprintf(stderr, "%s\n", mysql_error(con));
return 1;
}
mysql_query(con, "SELECT latitud FROM BARCOS ");
res = mysql_store_result(con);
printf("latitud:\n");
while((row = mysql_fetch_row(res)) !=0)
printf("%s\n", row[0]);
float lat = row[0] ? atof(row[0]) : 0.0;
printf("%f", lat);
mysql_free_result(res);
mysql_close(con);
return 0;
}
What is res after
res = mysql_store_result(con); // res could be NULL, you do not check the result
Then you fetch all rows and print them
while((row = mysql_fetch_row(res)) !=0) // no braces, that means, ...
printf("%s\n", row[0]); // print only the first element from each row
but afterwards you call
//row is NULL here, because you exhausted the set in the loop before
float lat = row[0] ? atof(row[0]) : 0.0;
and you cannot dereference a NULL pointer.
From mysql_store_result:
Return Values
A pointer to a MYSQL_RES result structure with the results. NULL if the statement did not return a result set or an error occurred.
From mysql_fetch_row:
Return Values
A MYSQL_ROW structure for the next row, or NULL (either if there are no more rows or when an error occurred).

C - alternative using snprintf to prepare MySQL statements?

I've been tearing my hair out for a while on this one. The C code is called from a bash script, which loops through a command's output in a while loop and passes variables to the C script as args. It goes through a list and partitions data properly. I've been using the C MySQL api, and up until now everything has been relatively straight forward. It tries to run a SELECT(EXISTS) command to dictate whether to input a new row, or update an existing one.
I have typed the command into MySQL terminal and it works perfectly. I have even printf'd it and copied the command directly into the terminal. It works....
So why then, am I getting Syntax errors? I've tried escaping fields and input using backticks, single quotes and double quotes and I'm still getting this dumbounding error. I thought maybe it was something to do with the null space? But I'm at my witts end. Here's the code, any advice would be greatly appreciated :)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <mysql/mysql.h>
const int MAXLEN = 100;
/* Compile with:
gcc db.connect.c `mysql_config --libs` -O1
for the best results
*/
/* Function definitions for later */
void finish_with_error(MYSQL *con);
int send_query(MYSQL *con, char query[MAXLEN]);
/* If any SQL commands fail, return an error message */
void finish_with_error(MYSQL *con)
{
fprintf(stderr, "%s\n", mysql_error(con));
mysql_close(con);
exit(1);
}
/* Helper function to send queries to MySQL database */
int send_query(MYSQL *con, char query[MAXLEN])
{
if (mysql_query(con, query)) {
finish_with_error(con);
}
return 0;
}
int main(int argc, char ** argv)
{
// Establish MySQL API connection, if not- fail with err
MYSQL *con = mysql_init(NULL);
if (con == NULL) {
finish_with_error(con);
}
// Connection string.
if (mysql_real_connect(con, "localhost", "user", "password",
NULL, 0, NULL, 0) == NULL){
finish_with_error(con);
}
if (argv[1] == NULL){
printf("No query passed, terminating script \n");
return 1;
}
if (argv[1] != NULL) {
if( strcmp( argv[1], "--help" ) == 0 ) {
printf("This program was created to interact with MySQL, by checking and updating live network stats\n");
printf("It has 2 parameters, an IP address to look in the database for and a value to update a field by, \
if that IP address is found. ");
printf("If the value is not found, the program will insert a new row.");
return 1;
}
// Works out how much memory to allocate to buffer for snprintf
// Originally cmd_len was 65- as this was the amount of bits needed by the address string.
// This was changed to MAXLEN to prevent SEGFAULTS and give the function breathing room.
size_t cmd_len = MAXLEN;
size_t param_len = sizeof(argv[2]);
size_t q_len = cmd_len + param_len;
// Allocates that memory to a buffer, referenced as query
char *query = malloc(sizeof(char) * q_len);
snprintf(query, q_len, "SELECT EXISTS(SELECT * FROM `analytics`.`live` WHERE `foreign_addr` = `%s`)", argv[1]);
printf("%s\n", query);
send_query(con, query);
free(query);
// Used to store the result of the MySQL select commands
MYSQL_RES *result = mysql_store_result(con);
if (result == NULL) {
finish_with_error(con);
}
// num_fields stores the number of fields, i and x are counters, answer is 1 or 0
int num_fields = mysql_num_fields(result);
int i = 0;
// Loops through each row in the answer statement.
// There will only be one row in the answer, which will be 1 or 0
// Basically, if the IP is found.
MYSQL_ROW row;
while ((row = mysql_fetch_row(result))){
for (i=0; i<num_fields; i++) {
// If the IP isn't in the table
if(!atoi(row[i]))
send_query(con, argv[1]);
// If the IP is already in the table
if(atoi(row[i])) {
snprintf(query, q_len, "UPDATE analytics.live SET count=count+1 WHERE foreign_addr = '%s'", argv[1]);
printf("%s\n", query);
free(query);
snprintf(query, q_len, "UPDATE analytics.live SET dat_sent = dat_sent + %s", argv[2]);
printf("%s\n", query);
free(query);
}
}
}
mysql_close(con);
return 1;
}
mysql_close(con);
return 0;
}

C mysql_num_rows() Segmentation fault

I'm trying to execute MySQL query in C, however I get a Segmentation fault while calling mysql_num_rows().
Here's the code I'm using:
char *username = "test#mail.com";
char *password = "pass";
char query[1000];
int len;
char *q = "SELECT * FROM Users WHERE `Email` = '%s' AND `Password` = '%s'";
len = snprintf(query, strlen(q) + strlen(username) + strlen(password), q, username, password);
MYSQL_RES *result;
if (db_query(query, result))
{
if (result != NULL)
{
int test_count = mysql_num_rows(result);
printf("%d\n", test_count);
}
}
else
{
printf("Query error\n");
}
And here is the db_query() function:
bool db_query(const char *query, MYSQL_RES *result)
{
if (mysql_query(db_connection, query))
{
printf("mysql_query(): Error %u: %s\n", mysql_errno(db_connection), mysql_error(db_connection));
return false;
}
if (!(result = mysql_store_result(db_connection)))
{
printf("mysql_store_result(): Error %u: %s\n", mysql_errno(db_connection), mysql_error(db_connection));
return false;
}
return true;
}
I've tested the query and the problem isn't there, the connection is initiated too. Any ideas?
Thanks!
Your problem is here, in the db_query function:
if (!(result = mysql_store_result(db_connection)))
The assignment to result has no visible effect in the function's caller - you're passing a pointer by value, changing the value of result in the callee doesn't do anything to result in the caller.
You need to change your function to take a pointer-to-pointer, and adapt the call site and the db_query function.
bool db_query(const char *query, MYSQL_RES **result)
{
...
if (!(*result = mysql_store_result(db_connection)))
...
}
Any changes to result in your db_query function are not reflected back to the caller, hence it will still contain the arbitrary value it had when it was created (as an auto variable with no initialisation.
If you want to change the value and have it reflected back, you should pass a double pointer to it then dereference the double pointer to get at the actual value.
Even better would be to return the result value and use its NULL/non-NULL status for a success code rather than returning true/false.

Using MySQL C API - check success of inserting rows using prepared statements

I'm beginning to learn how to use the MySQL C API and have encountered prepared statements. I haven't used these before in other languages so it's all new to me.
I've looked online and I've figured out how to use prepared statements to retrieve data from a SELECT query, and now what I'm trying to do is to INSERT some data and find out if it was successful. I've got the first part pretty much down, but my question is: how can I find out if my INSERT was successfully executed?
I've had a read through some MySQL documents for the C API/prepared statements - and had a Google/search on SO. All I've been able to find examples of are SELECT prepared statements and nothing more.
I've attached some code that I've created, which successfully inserts a row.
#include <mysql/mysql.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "session.h"
char *createSessionId(MYSQL *dbc) {
const char *sPS2 = "INSERT INTO `sessions` (`SessionID`) VALUES (?)";
char *sID;
MYSQL_STMT *stmt;
MYSQL_BIND param[1];
unsigned long l = 32;
// Allocate a statement handle
stmt = mysql_stmt_init(dbc);
if(stmt == NULL) {
printf("Unable to create new session: Could not init statement handle\n");
return NULL;
}
// Init
memset(param, 0, sizeof(param));
sID = malloc(33);
sprintf(sID, "01234567890123456789012345678901");
if(mysql_stmt_prepare(stmt, sPS2, strlen(sPS2)) != 0) {
printf("Unable to create new session: Could not prepare statement\n");
return NULL;
}
// Initialise param structure
param[0].buffer_type = MYSQL_TYPE_VARCHAR;
param[0].buffer = sID;
param[0].is_unsigned = 0;
param[0].is_null = 0;
param[0].length = &l;
// Bind param structure to statement
if(mysql_stmt_bind_param(stmt, param) != 0) {
printf("Unable to create new session: Could not bind parameters\n");
return NULL;
}
// Execute prepared statement
if(mysql_stmt_execute(stmt) != 0) {
printf("Unable to create new session: Could not execute statement\n");
return NULL;
}
mysql_stmt_free_result(stmt);
mysql_stmt_close(stmt);
return sID;
}
You already have the code in
if(mysql_stmt_execute(stmt) != 0) {
printf("Unable to create new session: Could not execute statement\n");
return NULL;
}
If that fails, you didn't insert any rows. The docs contain a full example
You can also use mysql_stmt_affected_rows() after a successful mysql_stmt_execute() to find out how many rows were insterted/updated/deleted.