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.
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 have an SQL callback function after querying the database and I'm then trying to stuff that data into a TCL dictionary so I can capture it in a TCL script.
I have this callback function which gets called for each select and it adds the column as key with it's value. I pass the Tcl_Interp* as the data arg.
int callback(void *interp, int argc, char **argv, char **azColName)
{
int i = 0;
Tcl_Obj *dictPtr = Tcl_NewDictObj();
for (i = 0; i < argc; i++) {
Tcl_Obj *key = Tcl_NewStringObj(azColName[i], -1);
Tcl_Obj *value = Tcl_NewStringObj((argv[i] ? argv[i] : "null"), -1);
Tcl_DictObjPut((Tcl_Interp*) interp, dictPtr, key, value);
}
return TCL_OK;
}
The problem is that when running this command like ...
set res [mycommand getrow]
foreach i $res {
puts "i = $i"
}
I see that the callback does get called and the correct key/val's are being added, but the result that gets printed is ...
i = 0
Am I missing anything here? I'm expecting my key (column) and val's (row) to be listed.
You're missing a call to Tcl_SetObjResult, which is probably best immediately before the function returns TCL_OK.
int callback(void *interp, int argc, char **argv, char **azColName)
{
int i = 0;
Tcl_Obj *dictPtr = Tcl_NewDictObj();
for (i = 0; i < argc; i++) {
Tcl_Obj *key = Tcl_NewStringObj(azColName[i], -1);
Tcl_Obj *value = Tcl_NewStringObj((argv[i] ? argv[i] : "null"), -1);
// Should handle refcounts correctly for keys
Tcl_IncrRefCount(key);
// Don't need to pass an interp; that's just for errors
Tcl_DictObjPut(NULL, dictPtr, key, value);
Tcl_DecrRefCount(key);
}
// This is the critical line
Tcl_SetObjResult((Tcl_Interp*) interp, dictPtr);
return TCL_OK;
}
The reason why Tcl_DictObjPut doesn't need an interp here? We can (trivially) prove that it never produces an error; the dictPtr argument is always a well-formed dictionary (and unshared, but getting that wrong would cause a panic).
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 am making a function in C using the MariaDB C-connector API, to allow users to enter a new username and password and have them stored in a MariaDB/MySQL database. I used strcpy_s() and strcat_s() to concatenate several strings together inside a buffer in order to produce the MariaDB/MySQL query to place the information into the database. This causes an 'unknown column in field list' error. I had asked a similar question on here before when I was implementing the function using "foo" and "bar" as preset strings, and the answer I received (which worked) was to change "foo" and "bar" to "'foo'" and "'bar'". This worked. Is there a way to have strings be taken from the user at run-time in the "'foo'" form versus the "foo" form? Below is the relevant code for the function.
void NEW_PLAYER(MYSQL *con)
{
char NAME[16];
char PASSWORD[16];
char ch = NULL;
unsigned int i = 0;
printf("New Username: ");
while (ch != '\n') // terminates when the user presses enter
{
ch = getchar();
NAME[i] = ch;
i++;
}
NAME[i] = '\0';
i = 0;
ch = NULL;
printf("New Password: ");
while (ch != '\n') // terminates if user hits enter
{
ch = getchar();
PASSWORD[i] = ch;
i++;
}
PASSWORD[i] = '\0';
printf("%s", NAME);
printf("%s", PASSWORD);
char TEMP[256];
char str1[] = "INSERT INTO PLAYERS VALUES(";
strcpy_s(TEMP, str1); strcat_s(TEMP, NAME);
strcat_s(TEMP, ", ");
strcat_s(TEMP, PASSWORD);
strcat_s(TEMP, " ,0, 0, 0, 1)");
if (mysql_query(con, TEMP)) {
fprintf(stderr, "%s\n", mysql_error(con));
exit(-1);
}
}
You need quotes around literals in the INSERT statements.
If NAME, PASSWORD, and/orTEMP` is not big enough, memory will be corrupted.
You must to escape certain special characters (esp, quotes) in the NAME or PASSWORD.
C is a terribly low language to be playing with MySQL in. In most languages that entire function, with the fixes, would take 5-10 lines of code.
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.