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.
Related
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.
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;
}
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.
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.
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.