#include "mysqld_error.h"
#include "config.h" /* EXT, from the common-c-lib */
#include "strings_utils.h" /* EXT, from the common-c-lib */
#include "mysql_header.h" /* INT, header for _this_ file */
#include "logging.h" /* INT, specific LOG service */
static void set_last_error (struct smysql *mysql_info);
static void clear_error_reporting (struct smysql *mysql_info);
static void copy_error (struct smysql *mysql_info, char *m);
static int standard_query (struct smysql *mysql_info, char* query);
/*! \brief Last MySql error message.
*/
char mysql_error_str[MYSQL_MAX_ERROR_SIZE];
/*! \brief Last MySql error code.
*/
int mysql_error_errno;
/*! \brief Clear all error reporting data.
\author Denis BEURIVE
*/
static void clear_error_reporting (struct smysql *mysql_info)
{
my_syslog (
LEVEL_FULL_VERBOSE,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Calling clear_error_reporting().");
mysql_info->last_error_int = 0;
memset ((void*)mysql_info->last_error_str, 0, MYSQL_MAX_ERROR_SIZE);
mysql_error_errno = 0;
memset ((void*)mysql_error_str, 0, MYSQL_MAX_ERROR_SIZE);
}
/*! \brief Safely copy error message from MySql.
\author Denis BEURIVE
\param mysql_info Pointer to a Mysql management structure.
*/
static void set_last_error (struct smysql *mysql_info)
{
const char *err;
int merror;
my_syslog (
LEVEL_FULL_VERBOSE,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Calling set_last_error().");
err = NULL;
merror = 0;
clear_error_reporting (mysql_info);
merror = mysql_errno(mysql_info->handler);
err = mysql_error(mysql_info->handler);
mysql_info->last_error_int = merror;
if (err != NULL) { strncpy (mysql_info->last_error_str, err, MYSQL_MAX_ERROR_SIZE); }
else { strncpy (mysql_info->last_error_str, "No error message available", MYSQL_MAX_ERROR_SIZE); }
mysql_error_errno = mysql_info->last_error_int;
strcpy (mysql_error_str, mysql_info->last_error_str);
return;
}
/*! \brief Safely copy a specific error message to the error reporting system. This function is intended to
set functional error message (that does not have MySql system error defined).
\author Denis BEURIVE
\param mysql_info mysql_info Pointer to a Mysql management structure.
\param m Pointer to zero terminated string of characters that represents the error message to report.
*/
static void copy_error (struct smysql *mysql_info, char *m)
{
my_syslog (
LEVEL_FULL_VERBOSE,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Calling copy_error().");
clear_error_reporting (mysql_info);
mysql_info->last_error_int = 0;
mysql_error_errno = 0;
strncpy (mysql_info->last_error_str, m, MYSQL_MAX_ERROR_SIZE);
strcpy (mysql_error_str, mysql_info->last_error_str);
}
/*! \brief Initialise the Mysql management structure.
\author Denis BEURIVE
\param mysql_info Pointer to a Mysql management structure.
*/
void init_mysql_structure (struct smysql *mysql_info)
{
my_syslog (
LEVEL_FULL_VERBOSE,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Calling init_mysql_structure().");
mysql_info->handler = NULL;
mysql_info->result = NULL;
mysql_info->num_fields = -1;
mysql_info->num_rows = -1;
mysql_info->last_connection_timestamp = 0;
mysql_info->connected = 0;
clear_error_reporting (mysql_info);
}
/*! \brief Connect to Mysql server.
\author Denis BEURIVE
\param mysql_info Pointer to a Mysql management structure that contains all the configuration for the
connection.
\return Upon successful completion, the function returns the value 0.
Otherwise it returns the value 1.
\warning
- You must call the function init_mysql_structure() first.
- Then you may:
- Call load_mysql_configuration_from_file() if you want to load the configuration
from a file.
- Don't forget to call disconnect_from_mysql() in order to free the allocated memory.
- We use the option CLIENT_FOUND_ROWS which modify the behavior of the function
mysql_affected_raws(). With this option, the function mysql_affected_raws() returns the
number of rows that matched the "WHERE" statement. In standard mode, the function
mysql_affected_raws() returns the number of rows that have been changed.
*/
int connect_to_mysql (struct smysql *mysql_info)
{
MYSQL *connect_res;
int retry;
retry = 0;
clear_error_reporting(mysql_info);
my_syslog (
LEVEL_FULL_VERBOSE,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Calling connect_to_mysql().");
while (retry <= mysql_info->connect_retry)
{
/* ----------------------------------------------- */
/* Free allocated memory, if necessary */
/* ----------------------------------------------- */
if (mysql_info->handler != NULL) { mysql_close(mysql_info->handler); mysql_info->handler = NULL; }
/* ----------------------------------------------- */
/* Initialize the handler structure */
/* ----------------------------------------------- */
mysql_info->handler = mysql_init(mysql_info->handler);
if (mysql_info->handler == NULL)
{
set_last_error (mysql_info);
mysql_info->connected = 0;
my_syslog (
LEVEL_ERROR,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Could not connect to the MySql server - mysql_init() failed (%s). This should _NOT_ happen!",
mysql_error_str);
return 1;
}
/* ----------------------------------------------- */
/* Set the connection timeout */
/* ----------------------------------------------- */
if (mysql_options(
mysql_info->handler,
MYSQL_OPT_CONNECT_TIMEOUT,
(char*)&(mysql_info->connect_timeout)) != 0)
{
set_last_error (mysql_info);
mysql_info->connected = 0;
my_syslog (
LEVEL_ERROR,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Could not connect to the MySql server - mysql_options() failed (%s). This should _NOT_ happen!",
mysql_error_str);
return 1;
}
/* ----------------------------------------------- */
/* Connect to mysql server */
/* Note: the option CLIENT_FOUND_ROWS modify the */
/* behavior of function mysql_affected_rows(). */
/* ----------------------------------------------- */
connect_res = mysql_real_connect (
mysql_info->handler,
mysql_info->host,
mysql_info->user,
mysql_info->passwd,
mysql_info->db,
mysql_info->port,
mysql_info->socket,
CLIENT_FOUND_ROWS
);
if (connect_res != NULL) { break; }
/* ----------------------------------------------- */
/* Could not connect to server, try again */
/* ----------------------------------------------- */
my_syslog (
LEVEL_DEBUG,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Try to re-connect to MySql server (%d / %d).",
retry,
mysql_info->connect_retry);
sleep (mysql_info->connect_sleep);
retry++;
}
/* ------------------------------------------------- */
/* We may not be connected ... */
/* ------------------------------------------------- */
if (retry > mysql_info->connect_retry)
{
set_last_error (mysql_info);
mysql_info->connected = 0;
my_syslog (
LEVEL_WARNING,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Could not open connection to the MySql server. Too many retries... I give up (%s).",
mysql_error_str);
return 1;
}
my_syslog (
LEVEL_INFO,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Connection to the MySql server successfully established.");
mysql_info->connected = 1;
return 0;
}
/*! \brief Close the connection with the Mysql server and free allocated memory.
\author Denis BEURIVE
\param mysql_info Pointer to a Mysql management structure.
*/
void disconnect_from_mysql(struct smysql *mysql_info)
{
my_syslog (
LEVEL_FULL_VERBOSE,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Calling disconnect_from_mysql().");
if (mysql_info->handler != NULL) { mysql_close(mysql_info->handler); mysql_info->handler = NULL; }
}
/*! \brief Execute SQL statements that does not return data (UPDATE, DELETE, INSERT, REPLACE and otjer).
\author Denis BEURIVE
\param mysql_info Pointer to a Mysql management structure.
\param query SQL statement to execute.
\return Upon successful completion, the function returns the number of rows matched by the "WHERE" statement.
Otherwize, the function may return one of the following values:
- MYSQL_REQUEST_SKIPED
- MYSQL_CONNECTION_LOST
- MYSQL_RECONNECTION_FAILED
- MYSQL_SQL_PROBLEM
- MYSQL_UNEXPECTED_ERROR
\warning
- Keep in mind that the function does __NOT__ return the number of rows that have been changed
by the request. The function returns the number of rows that matched the "WHERE" statement
(which may be different).
- If the connection to the MySql server is lost, then the client tries to reconnect. If it
can not reconnect, then the function returns on failure.
*/
static int standard_query (struct smysql *mysql_info, char* query)
{
int cr, n, err;
my_syslog (
LEVEL_FULL_VERBOSE,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Calling standard_query().");
#ifdef TEST_MYSQL_STANDART_QUERY
fprintf (stdout, "Testing mysql.c::standard_query()\n");
fprintf (stdout, " Next action: <1> Test the connection state (UP/DOWN).\n");
fprintf (stdout, " Next action: <2> If status is DOWN, test the delay since last disconnection.\n");
fprintf (stdout, " Next action: <3> If status is DOWN, skip request or try to reconnect.\n");
fprintf (stdout, "\n");
fprintf (stdout, " Press any key to continue.");
getchar();
fprintf (stdout, "\n\n");
#endif
clear_error_reporting(mysql_info);
my_syslog (
LEVEL_DEBUG,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Execute SQL request [%s].",
query);
/* ------------------------------------------------- */
/* If connection is down, skip request OR try to */
/* reconnect. */
/* ------------------------------------------------- */
if (! mysql_info->connected)
{
time_t delta = time(NULL) - mysql_info->last_connection_timestamp;
if (mysql_info->reconnection_delay == 0) { delta = 0; }
my_syslog (
LEVEL_DEBUG,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"MySql connection is down since %u seconds.",
delta);
if ((delta > mysql_info->reconnection_delay) || (mysql_info->reconnection_delay == 0))
{
my_syslog (
LEVEL_DEBUG,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Try to re-connect to MySql server (%d / %d).",
delta,
mysql_info->reconnection_delay);
/* ------------------------------------------ */
/* Try to reconnect */
/* ------------------------------------------ */
disconnect_from_mysql (mysql_info);
if (connect_to_mysql(mysql_info) == 1)
{
my_syslog (
LEVEL_DEBUG,
HIGH_LEVEL,
__FILE__,
__LINE__,
"Could not re-connect to the MySql server.");
/* ---------------------------------------- */
/* Connection to the server is down */
/* ---------------------------------------- */
mysql_info->last_connection_timestamp = time(NULL);
mysql_info->connected = 0;
copy_error (mysql_info, "Could not reconnect to the MySql server.");
return MYSQL_RECONNECTION_FAILED;
}
else
{
mysql_info->last_connection_timestamp = 0;
mysql_info->connected = 1;
}
}
else {
my_syslog (
LEVEL_DEBUG,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"%u < %u => Do not try to reconnect. Skip SQL request.",
delta,
mysql_info->reconnection_delay);
copy_error (mysql_info, "SQL request skiped (this is normal, see configuration)");
return MYSQL_REQUEST_SKIPED;
}
}
/* ------------------------------------------------- */
/* At this point, the connection should be up */
/* ------------------------------------------------- */
#ifdef TEST_MYSQL_STANDART_QUERY
fprintf (stdout, "Testing mysql.c::standard_query()\n");
fprintf (stdout, " Next action: <1> Perform the SQL query\n");
fprintf (stdout, "\n");
fprintf (stdout, " Press any key to continue.");
getchar();
fprintf (stdout, "\n\n");
#endif
while (1)
{
clear_error_reporting(mysql_info);
/* ------------------------------------------------- */
/* Make sure that we are connected */
/* ------------------------------------------------- */
if (mysql_info->handler == NULL)
{
set_last_error (mysql_info);
my_syslog (
LEVEL_ERROR,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Connection is not established, as it should be (%s)! This should _NOT_ happen (it is REALLY strange)!",
mysql_error_str);
return MYSQL_UNEXPECTED_ERROR;
}
/* ------------------------------------------------- */
/* Send the request to mysql server */
/* ------------------------------------------------- */
cr = mysql_query(mysql_info->handler, query);
if (cr != 0)
{
set_last_error (mysql_info);
my_syslog (
LEVEL_DEBUG,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Query [%s] failed: %s (%d -- CR_SERVER_GONE_ERROR=%d, CR_SERVER_LOST=%d, CR_CONNECTION_ERROR=%d, ER_SERVER_SHUTDOWN=%d) (%s)",
query,
mysql_info->last_error_str,
mysql_info->last_error_int,
CR_SERVER_GONE_ERROR,
CR_SERVER_LOST,
CR_CONNECTION_ERROR,
ER_SERVER_SHUTDOWN,
mysql_error_str);
/* ----------------------------------------------- */
/* The server may have gone away... */
/* ----------------------------------------------- */
err = mysql_errno(mysql_info->handler);
if ((err == CR_SERVER_GONE_ERROR)
||
(err == CR_SERVER_LOST)
||
(err == CR_CONNECTION_ERROR)
||
(err == ER_SERVER_SHUTDOWN))
{
my_syslog (
LEVEL_WARNING,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Connection lost with MySql server.");
disconnect_from_mysql (mysql_info);
if (connect_to_mysql(mysql_info) == 1)
{
/* ------------------------------------------- */
/* Connection to the server is down */
/* ------------------------------------------- */
mysql_info->last_connection_timestamp = time(NULL);
mysql_info->connected = 0;
set_last_error (mysql_info);
my_syslog (
LEVEL_DEBUG,
HIGH_LEVEL,
__FILE__,
__LINE__,
"Could not re-connect to the MySql server (%s).",
mysql_error_str);
return MYSQL_CONNECTION_LOST;
}
my_syslog (
LEVEL_INFO,
HIGH_LEVEL,
__FILE__,
__LINE__,
"Connection to the MySql server successfully established.");
mysql_info->last_connection_timestamp = 0;
mysql_info->connected = 1;
continue;
}
else {
set_last_error (mysql_info);
my_syslog (
LEVEL_ERROR,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Some SQL error occured, other that disconnection (%s). This should _NOT_ happen!",
mysql_error_str);
return MYSQL_SQL_PROBLEM;
}
}
break;
}
/* ------------------------------------------------- */
/* Get the number of affected rows */
/* ------------------------------------------------- */
n = mysql_affected_rows(mysql_info->handler);
my_syslog (
LEVEL_VERBOSE,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Number of affected row(s) is %d.",
n);
return n;
}
/*! \brief Execute an UPDATE SQL statement.
\author Denis BEURIVE
\param mysql_info Pointer to a Mysql management structure.
\param query SQL statement to execute.
\return Upon successful completion, the function returns the number of rows matched by the "WHERE"
statement.
Otherwize, the function may return one of the following values:
- MYSQL_REQUEST_SKIPED
- MYSQL_CONNECTION_LOST
- MYSQL_RECONNECTION_FAILED
- MYSQL_SQL_PROBLEM
- MYSQL_UNEXPECTED_ERROR
\warning Keep in mind that the function does __NOT__ return the number of rows that have been changed by
the request. The function returns the number of rows that matched the "WHERE" statement
(which may be different).
*/
int sql_update (struct smysql *mysql_info, char* query) { return standard_query (mysql_info, query); }
/*! \brief Execute an INSERT SQL statement.
\author Denis BEURIVE
\param mysql_info Pointer to a Mysql management structure.
\param query SQL statement to execute.
\return Upon successful completion, the function returns the number of rows matched by the "WHERE"
statement.
Otherwize, the function may return one of the following values:
- MYSQL_REQUEST_SKIPED
- MYSQL_CONNECTION_LOST
- MYSQL_RECONNECTION_FAILED
- MYSQL_SQL_PROBLEM
- MYSQL_UNEXPECTED_ERROR
\warning Keep in mind that the function does __NOT__ return the number of rows that have been changed by
the request. The function returns the number of rows that matched the "WHERE" statement
(which may be different).
*/
int sql_insert (struct smysql *mysql_info, char* query) { return standard_query (mysql_info, query); }
/*! \brief Execute a REPLACE SQL statement.
\author Denis BEURIVE
\param mysql_info Pointer to a Mysql management structure.
\param query SQL statement to execute.
\return Upon successful completion, the function returns the number of rows matched by the "WHERE"
statement.
Otherwize, the function may return one of the following values:
- MYSQL_REQUEST_SKIPED
- MYSQL_CONNECTION_LOST
- MYSQL_RECONNECTION_FAILED
- MYSQL_SQL_PROBLEM
- MYSQL_UNEXPECTED_ERROR
\warning Keep in mind that the function does __NOT__ return the number of rows that have been changed by
the request. The function returns the number of rows that matched the "WHERE" statement
(which may be different).
*/
int sql_replace (struct smysql *mysql_info, char* query) { return standard_query (mysql_info, query); }
/*! \brief Execute a DELETE SQL statement.
\author Denis BEURIVE
\param mysql_info Pointer to a Mysql management structure.
\param query SQL statement to execute.
\return Upon successful completion, the function returns the number of rows matched by the "WHERE"
statement.
Otherwize, the function may return one of the following values:
- MYSQL_REQUEST_SKIPED
- MYSQL_CONNECTION_LOST
- MYSQL_RECONNECTION_FAILED
- MYSQL_SQL_PROBLEM
- MYSQL_UNEXPECTED_ERROR
\warning Keep in mind that the function does __NOT__ return the number of rows that have been changed by
the request. The function returns the number of rows that matched the "WHERE" statement
(which may be different).
*/
int sql_delete (struct smysql *mysql_info, char* query) { return standard_query (mysql_info, query); }
/*! \brief Execute a SELECT SQL statement.
\author Denis BEURIVE
\param mysql_info Pointer to a Mysql management structure.
\param query SQL statement to execute.
\return Upon successful completion, the function returns the number of selected items.
Otherwize, the function may return one of the following values:
- MYSQL_REQUEST_SKIPED
- MYSQL_CONNECTION_LOST
- MYSQL_RECONNECTION_FAILED
- MYSQL_SQL_PROBLEM
- MYSQL_UNEXPECTED_ERROR
\warning Upon successful completion, the fonction stores results into the MySql data structure
mysql_info->result. You must call the function mysql_free_result() in order to
free all allocated memory. Note that if an error occured, you must __NOT__ call
mysql_free_result().
*/
int sql_select (struct smysql *mysql_info, char* query)
{
int cr, err;
my_syslog (
LEVEL_FULL_VERBOSE,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Calling sql_select().");
clear_error_reporting (mysql_info);
my_syslog (
LEVEL_DEBUG,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Execute SQL request [%s].",
query);
/* ------------------------------------------------- */
/* If connection is down, skip request OR try to */
/* reconnect. */
/* ------------------------------------------------- */
#ifdef TEST_MYSQL_STANDART_QUERY
fprintf (stdout, "Testing mysql.c::sql_select()\n");
fprintf (stdout, " Next action: <1> Test the connection state (UP/DOWN).\n");
fprintf (stdout, " Next action: <2> If status is DOWN, test the delay since last disconnection.\n");
fprintf (stdout, " Next action: <3> If status is DOWN, skip request or try to reconnect.\n");
fprintf (stdout, "\n");
fprintf (stdout, " Press any key to continue.");
getchar();
fprintf (stdout, "\n\n");
#endif
if (! mysql_info->connected)
{
time_t delta = time(NULL) - mysql_info->last_connection_timestamp;
if (mysql_info->reconnection_delay == 0) { delta = 0; }
my_syslog (
LEVEL_DEBUG,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"MySql connection is down since %u seconds.",
delta);
if ((delta > mysql_info->reconnection_delay) || (mysql_info->reconnection_delay == 0))
{
my_syslog (
LEVEL_DEBUG,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Try to re-connect to MySql server (%d / %d).",
delta,
mysql_info->reconnection_delay);
/* ------------------------------------------ */
/* Try to reconnect */
/* ------------------------------------------ */
disconnect_from_mysql (mysql_info);
if (connect_to_mysql(mysql_info) == 1)
{
my_syslog (
LEVEL_DEBUG,
HIGH_LEVEL,
__FILE__,
__LINE__,
"Could not re-connect to the MySql server.");
/* ------------------------------------------- */
/* Connection to the server is down */
/* ------------------------------------------- */
mysql_info->last_connection_timestamp = time(NULL);
mysql_info->connected = 0;
copy_error (mysql_info, "Could not reconnect to database");
return MYSQL_RECONNECTION_FAILED;
}
else
{
mysql_info->last_connection_timestamp = 0;
mysql_info->connected = 1;
}
}
else {
my_syslog (
LEVEL_DEBUG,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"%u < %u => Do not try to reconnect. Skip SQL request.",
delta,
mysql_info->reconnection_delay);
copy_error (mysql_info, "SQL request skiped (this is normal - see configuration)");
return MYSQL_REQUEST_SKIPED;
}
}
/* ------------------------------------------------- */
/* At this point, the connection should be up */
/* ------------------------------------------------- */
#ifdef TEST_MYSQL_STANDART_QUERY
fprintf (stdout, "Testing mysql.c::sql_select()\n");
fprintf (stdout, " Next action: <1> Perform the SQL query.\n");
fprintf (stdout, "\n");
fprintf (stdout, " Press any key to continue.");
getchar();
fprintf (stdout, "\n\n");
#endif
while (1)
{
mysql_info->num_fields = -1;
mysql_info->num_rows = -1;
clear_error_reporting(mysql_info);
/* ------------------------------------------------- */
/* Make sure that we are connected */
/* ------------------------------------------------- */
if (mysql_info->handler == NULL)
{
set_last_error (mysql_info);
my_syslog (
LEVEL_ERROR,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Connection is not established, as it should be (%s)! This should _NOT_ happen (it is REALLY strange)!",
mysql_error_str);
return MYSQL_UNEXPECTED_ERROR;
}
/* ------------------------------------------------- */
/* Send the request to mysql server */
/* ------------------------------------------------- */
cr = mysql_query(mysql_info->handler, query);
if (cr != 0)
{
set_last_error (mysql_info);
my_syslog (
LEVEL_DEBUG,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Query [%s] failed: %s (%d -- CR_SERVER_GONE_ERROR=%d, CR_SERVER_LOST=%d, CR_CONNECTION_ERROR=%d, ER_SERVER_SHUTDOWN=%d) (%s)",
query,
mysql_info->last_error_str,
mysql_info->last_error_int,
CR_SERVER_GONE_ERROR,
CR_SERVER_LOST,
CR_CONNECTION_ERROR,
ER_SERVER_SHUTDOWN,
mysql_error_str);
/* ----------------------------------------------- */
/* The server may have gone away */
/* ----------------------------------------------- */
err = mysql_errno(mysql_info->handler);
if ((err == CR_SERVER_GONE_ERROR)
||
(err == CR_SERVER_LOST)
||
(err == CR_CONNECTION_ERROR)
||
(err == ER_SERVER_SHUTDOWN))
{
my_syslog (
LEVEL_WARNING,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Connection lost with the MySql server.");
disconnect_from_mysql (mysql_info);
if (connect_to_mysql(mysql_info) == 1)
{
my_syslog (
LEVEL_DEBUG,
HIGH_LEVEL,
__FILE__,
__LINE__,
"Could not re-connect to the MySql server.");
/* ------------------------------------------- */
/* Connection to the server is down */
/* ------------------------------------------- */
mysql_info->last_connection_timestamp = time(NULL);
mysql_info->connected = 0;
copy_error (mysql_info, "Could not reconnect to the database");
return MYSQL_RECONNECTION_FAILED;
}
my_syslog (
LEVEL_INFO,
HIGH_LEVEL,
__FILE__,
__LINE__,
"Connection to the MySql server successfully established.");
mysql_info->last_connection_timestamp = time(NULL);
mysql_info->connected = 0;
continue;
}
else {
set_last_error (mysql_info);
my_syslog (
LEVEL_ERROR,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Some SQL error occured, other that disconnection (%s). This should _NOT_ happen!",
mysql_error_str);
return MYSQL_SQL_PROBLEM;
}
}
break;
}
/* ------------------------------------------------- */
/* Get selected data */
/* ------------------------------------------------- */
mysql_info->result = mysql_store_result(mysql_info->handler);
if (mysql_info->result)
{
mysql_info->num_rows = mysql_num_rows (mysql_info->result);
mysql_info->num_fields = mysql_num_fields (mysql_info->result);
}
else
{
set_last_error (mysql_info);
my_syslog (
LEVEL_ERROR,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Some SQL error occured, other that disconnection (%s). This should _NOT_ happen!",
mysql_error_str);
return MYSQL_UNEXPECTED_ERROR;
}
return mysql_info->num_rows;
}
/*! \brief Load MySql configuration from a given configuration file.
\author Denis BEURIVE
\param path Path to the configuration file to parse.
\param mysql_info Pointer to a MySql configuration data structure unsed to store the loaded configuration.
\param tags Pointer to an array of zero terminated strinfs of characters. Each string represents one tag
to load from the configuration file. Tags must be written in the following order:
- 0 - Host name of the server that runs the database server.
- 1 - User used to log to the database server.
- 2 - Password used to log to the database server.
- 3 - Name of the database.
- 4 - TCP port number use to connect to the database server.
- 5 - Path to the UNIX socket used for local connection to the database server.
- 6 - Number of connection retry.
- 7 - Connection timeout in seconds.
- 8 - Delay of inactivity between 2 connection attempts.
- 9 - Number of seconds between 2 reconnection attempts.
The size of the array is MYSQL_CONFIGURATION_TAG_NUMBER.
\return Upon successful completion, the function returns the value 0.
Otherwize, the function returns the value 1.
\warning If you do not want to specify a log file in the configuration file, then set the value of the
tag "log-file" (index 10 in the array 'tags') to "desactivate logs".
*/
int load_mysql_configuration_from_file (
char *path,
struct smysql *mysql_info,
char *tags[MYSQL_CONFIGURATION_TAG_NUMBER]
)
{
int res, e;
char *value;
char big_buffer[8000];
/* ------------------------------------------------- */
/* Clear error reporting service */
/* ------------------------------------------------- */
clear_error_reporting(mysql_info);
/* ------------------------------------------------- */
/* Load the configuration file into memory */
/* ------------------------------------------------- */
if (parse_configuration_file (path, &e) != PARSE_CONFIGURATION_OK)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"Could not load the configuration file <%s> - %s",
path,
strerror(e));
return 1;
}
/* ------------------------------------------------- */
/* Set defaults */
/* ------------------------------------------------- */
memset ((void*)mysql_info->host, 0, DB_HOST_MAX_SIZE);
memset ((void*)mysql_info->user, 0, DB_USER_MAX_SIZE);
memset ((void*)mysql_info->passwd, 0, DB_PASSWD_MAX_SIZE);
memset ((void*)mysql_info->db, 0, DB_NAME_MAX_SIZE);
memset ((void*)mysql_info->socket, 0, MYSQL_SOCKET_FILE_MAX_SIZE);
mysql_info->port = 0;
mysql_info->connect_retry = 0;
mysql_info->connect_timeout = 0;
mysql_info->connect_sleep = 0;
mysql_info->reconnection_delay = 0;
/* ------------------------------------------------- */
/* Get the Host name */
/* ------------------------------------------------- */
res = get_value (tags[0], &value);
if (res == 0)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: tag '%s' is missing.",
path,
tags[0]);
return 1;
}
if (res == -1)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: Unexpected error while checking tag '%s'.",
path,
tags[0]);
return 1;
}
if (strlen(value) >= DB_HOST_MAX_SIZE)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: Tag '%s' is too long (buffer overflow avoided!).",
path,
tags[0]);
return 1;
}
strcpy (mysql_info->host, value);
/* ------------------------------------------------- */
/* Get the user name */
/* ------------------------------------------------- */
res = get_value (tags[1], &value);
if (res == 0)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: tag '%s' is missing.",
path,
tags[1]);
return 1;
}
if (res == -1)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: Unexpected error while checking tag '%s'",
path,
tags[1]);
return 1;
}
if (strlen(value) >= DB_USER_MAX_SIZE)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: Tag '%s' is too long (buffer overflow avoided!)",
path,
tags[1]);
return 1;
}
strcpy (mysql_info->user, value);
/* ------------------------------------------------- */
/* Get the password */
/* ------------------------------------------------- */
res = get_value (tags[2], &value);
if (res == 0)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: tag '%s' is missing",
path,
tags[2]);
return 1;
}
if (res == -1)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: Unexpected error while checking tag '%s'",
path,
tags[2]);
return 1;
}
if (strlen(value) >= DB_PASSWD_MAX_SIZE)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: Tag '%s' is too long (buffer overflow avoided!)",
path,
tags[2]);
return 1;
}
strcpy (mysql_info->passwd, value);
/* ------------------------------------------------- */
/* Get the database name */
/* ------------------------------------------------- */
res = get_value (tags[3], &value);
if (res == 0)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: tag '%s' is missing",
path,
tags[3]);
return 1;
}
if (res == -1)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: Unexpected error while checking tag '%s'",
path,
tags[3]);
return 1;
}
if (strlen(value) >= DB_NAME_MAX_SIZE)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: Tag '%s' is too long (buffer overflow avoided!)",
path,
tags[3]);
return 1;
}
strcpy (mysql_info->db, value);
/* ------------------------------------------------- */
/* Get the TCP port number */
/* ------------------------------------------------- */
res = get_value (tags[4], &value);
if (res == 0)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: tag '%s' is missing",
path,
tags[4]);
return 1;
}
if (res == -1)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: Unexpected error while checking tag '%s'",
path,
tags[4]);
return 1;
}
mysql_info->port = atoi(value);
/* ------------------------------------------------- */
/* Get the UNIX socket path */
/* ------------------------------------------------- */
res = get_value (tags[5], &value);
if (res == 0)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: tag '%s' is missing",
path,
tags[5]);
return 1;
}
if (res == -1)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: Unexpected error while checking tag '%s'",
path,
tags[5]);
return 1;
}
if (strlen(value) >= MYSQL_SOCKET_FILE_MAX_SIZE)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: Tag '%s' is too long (buffer overflow avoided!)",
path,
tags[5]);
return 1;
}
strcpy (mysql_info->socket, value);
/* ------------------------------------------------- */
/* Get number of connect retries */
/* ------------------------------------------------- */
res = get_value (tags[6], &value);
if (res == 0)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: tag '%s' is missing",
path,
tags[6]);
return 1;
}
if (res == -1)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: Unexpected error while checking tag '%s'",
path,
tags[6]);
return 1;
}
mysql_info->connect_retry = atoi(value);
/* ------------------------------------------------- */
/* Get the connect timout */
/* ------------------------------------------------- */
res = get_value (tags[7], &value);
if (res == 0)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: tag '%s' is missing",
path,
tags[7]);
return 1;
}
if (res == -1)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: Unexpected error while checking tag '%s'",
path,
tags[7]);
return 1;
}
mysql_info->connect_timeout = atoi(value);
/* ------------------------------------------------- */
/* Get the inactivity period between 2 connections */
/* ------------------------------------------------- */
res = get_value (tags[8], &value);
if (res == 0)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: tag '%s' is missing",
path,
tags[8]);
return 1;
}
if (res == -1)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: Unexpected error while checking tag '%s'",
path,
tags[8]);
return 1;
}
mysql_info->connect_sleep = atoi(value);
/* ------------------------------------------------- */
/* Get the reconnection delay */
/* ------------------------------------------------- */
res = get_value (tags[9], &value);
if (res == 0)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: tag '%s' is missing",
path,
tags[11]);
return 1;
}
if (res == -1)
{
snprintf (mysql_info->last_error_str, MYSQL_MAX_ERROR_SIZE,
"<%s>: Unexpected error while checking tag '%s'",
path,
tags[11]);
return 1;
}
mysql_info->reconnection_delay = atoi(value);
/* ------------------------------------------------- */
/* Log DEBUG data, if required */
/* ------------------------------------------------- */
memset ((void*)big_buffer, 0, 8000);
snprintf (big_buffer,
8000,
"host='%s', user='%s', passwd='%s', dbname='%s', port='%u', socket='%s', connect_retry='%d', connect_timeout='%d', connect_sleep='%d', reconnection_delay='%lu'",
mysql_info->host,
mysql_info->user,
mysql_info->passwd,
mysql_info->db,
mysql_info->port,
mysql_info->socket,
mysql_info->connect_retry,
mysql_info->connect_timeout,
mysql_info->connect_sleep,
mysql_info->reconnection_delay);
my_syslog (
LEVEL_DEBUG,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"MySql configuration is: %s",
big_buffer);
return 0;
}
/*! \brief Return the UNIX timestamp of the MySql server.
\author Denis BEURIVE
\param mysql_info Pointer to a 'struct smysql' that contains the Mysql configuration.
\param error Pointer to an integer used to store an error code.
Upon successful completion, the value pointed by 'error' is set to 0.
Otherwise, the value pointed by 'error' could be:
- MYSQL_REQUEST_SKIPED
- MYSQL_CONNECTION_LOST
- MYSQL_RECONNECTION_FAILED
- MYSQL_SQL_PROBLEM
- MYSQL_UNEXPECTED_ERROR
- MYSQL_NO_TIMESTAMP
\return Upon successful completion (the value pointed by 'error' is set to 0), then the function
returns the current UNIX timestamp of the MySql server. Otherwize, the function returns the
value 0.
*/
time_t get_unix_timestamp (struct smysql *mysql_info, int *error)
{
char sql[] = "select UNIX_TIMESTAMP(now())";
char integer1[32];
int n, e;
MYSQL_ROW row;
unsigned long int *length;
time_t ttstp;
my_syslog (
LEVEL_FULL_VERBOSE,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Calling get_unix_timestamp().");
*error = 0;
/* ----------------------------------------------------- */
/* Send request to MySql server */
/* ----------------------------------------------------- */
n = sql_select (mysql_info, sql);
switch (n)
{
case MYSQL_REQUEST_SKIPED:
my_syslog (
LEVEL_DEBUG,
HIGH_LEVEL,
__FILE__,
__LINE__,
"Timestamp request skiped.");
*error = MYSQL_REQUEST_SKIPED;
return 0;
case MYSQL_CONNECTION_LOST:
my_syslog (
LEVEL_DEBUG,
HIGH_LEVEL,
__FILE__,
__LINE__,
"Connection lost with the MySql server.");
*error = MYSQL_CONNECTION_LOST;
return 0;
case MYSQL_RECONNECTION_FAILED:
my_syslog (
LEVEL_DEBUG,
HIGH_LEVEL,
__FILE__,
__LINE__,
"Could not re-connect to the MySql server.");
*error = MYSQL_RECONNECTION_FAILED;
return 0;
case MYSQL_SQL_PROBLEM:
my_syslog (
LEVEL_DEBUG,
HIGH_LEVEL,
__FILE__,
__LINE__,
"Some SQL error occured.");
*error = MYSQL_SQL_PROBLEM;
return 0;
case MYSQL_UNEXPECTED_ERROR:
my_syslog (
LEVEL_DEBUG,
HIGH_LEVEL,
__FILE__,
__LINE__,
"Unexpected SQL error");
*error = MYSQL_UNEXPECTED_ERROR;
return 0;
case 0:
my_syslog (
LEVEL_DEBUG,
HIGH_LEVEL,
__FILE__,
__LINE__,
"Could not get UNIX timestamp (Request [%s]). Select returned no result.",
sql);
*error = MYSQL_UNEXPECTED_ERROR;
*error = MYSQL_NO_TIMESTAMP;
mysql_free_result(mysql_info->result);
return 0;
}
my_syslog (
LEVEL_VERBOSE,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Got %d result(s) (Request [%s])",
n,
sql);
/* ----------------------------------------------------- */
/* Copy the result */
/* ----------------------------------------------------- */
row = mysql_fetch_row (mysql_info->result);
length = mysql_fetch_lengths (mysql_info->result);
my_syslog (
LEVEL_VERBOSE,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Lengthes are: timestamp[%d]",
length[0]);
memset ((void*)integer1, 0, 32);
memcpy ((void*)integer1, (void*)row[0],
(length[0] < 32) ? length[0] : 31);
ttstp = (time_t)(string2unsigned_int (integer1, &e));
my_syslog (
LEVEL_DEBUG,
TERMINAL_LEVEL,
__FILE__,
__LINE__,
"Timestamp is %u.",
ttstp);
mysql_free_result(mysql_info->result);
return ttstp;
}