/* db_msql.c */ /* Implements accessing an mSQL 2.x database. */ #include "copyright.h" #include "autoconf.h" #include "mudconf.h" #include "config.h" #include "externs.h" #include "msql.h" /* See db_sql.h for details of what each of these functions do. */ /* Because we cannot trap error codes in mSQL, just error messages, we have * to compare against error messages to find out what really happened with * something. This particular error message is SERVER_GONE_ERROR in * mSQL's errmsg.h -- this needs to change if that API definition changes. */ #define MSQL_SERVER_GONE_ERROR "MSQL server has gone away" /* Number of times to retry a connection if we fail in the middle of * a query. */ #define MSQL_RETRY_TIMES 3 /* ------------------------------------------------------------------------ * Do the work here. */ void sql_shutdown() { if (mudstate.sql_socket == -1) return; msqlClose(mudstate.sql_socket); mudstate.sql_socket = -1; } int sql_init() { int result; /* Make sure we have valid config options. */ if (!mudconf.sql_host || !*mudconf.sql_host) return -1; if (!mudconf.sql_db || !*mudconf.sql_db) return -1; /* If we are already connected, drop and retry the connection, in * case for some reason the server went away. */ sql_shutdown(); /* Try to connect to the database host. If we have specified * localhost, use the Unix domain socket instead. */ if (!strcmp(mudconf.sql_host, (char *) "localhost")) result = msqlConnect(NULL); else result = msqlConnect(mudconf.sql_host); if (result == -1) { STARTLOG(LOG_ALWAYS, "SQL", "CONN") if (msqlErrMsg) { log_text((char *) "Failed connection to SQL server "); log_text(mudconf.sql_host); log_text((char *) ": "); log_text(msqlErrMsg); } ENDLOG return -1; } STARTLOG(LOG_ALWAYS, "SQL", "CONN") log_text((char *) "Connected to SQL server "); log_text(mudconf.sql_host); log_text((char *) ", socket fd "); log_number(result); ENDLOG mudstate.sql_socket = result; /* Select the database we want. If we can't get it, disconnect. */ if (msqlSelectDB(mudstate.sql_socket, mudconf.sql_db) == -1) { STARTLOG(LOG_ALWAYS, "SQL", "CONN") log_text((char *) "Failed db select: "); log_text(msqlErrMsg); ENDLOG msqlClose(mudstate.sql_socket); mudstate.sql_socket = -1; return -1; } STARTLOG(LOG_ALWAYS, "SQL", "CONN") log_text((char *) "SQL database selected: "); log_text(mudconf.sql_db); ENDLOG return (mudstate.sql_socket); } /* Special handling of separators -- identical to functions.h definition. */ #define print_sep(s,b,p) \ if (s) { \ if (s != '\r') { \ safe_chr(s,b,p); \ } else { \ safe_crlf(b,p); \ } \ } int sql_query(player, q_string, buff, bufc, row_delim, field_delim) dbref player; char *q_string; char *buff; char **bufc; char row_delim, field_delim; { m_result *qres; m_row row_p; int got_rows, got_fields; int i, j; int retries; /* If we have no connection, and we don't have auto-reconnect on * (or we try to auto-reconnect and we fail), this is an error * generating a #-1. Notify the player, too, and set the return code. */ if ((mudstate.sql_socket == -1) && (mudconf.sql_reconnect != 0)) { /* Try to reconnect. */ retries = 0; while ((retries < MSQL_RETRY_TIMES) && (mudstate.sql_socket == -1)) { sleep(1); sql_init(); retries++; } } if (mudstate.sql_socket == -1) { notify(player, "No SQL database connection."); if (buff) safe_str("#-1", buff, bufc); return -1; } if (!q_string || !*q_string) return 0; /* Send the query. */ got_rows = msqlQuery(mudstate.sql_socket, q_string); if ((got_rows == -1) && !strcmp(msqlErrMsg, MSQL_SERVER_GONE_ERROR)) { /* We got this error because the server died unexpectedly * and it shouldn't have. Try repeatedly to reconnect before * giving up and failing. This induces a few seconds of lag, * depending on number of retries; we put in the sleep() here * to see if waiting a little bit helps. */ STARTLOG(LOG_PROBLEMS, "SQL", "GONE") log_text("Connection died to SQL server on fd "); log_number(mudstate.sql_socket); ENDLOG retries = 0; mudstate.sql_socket = -1; while ((retries < MSQL_RETRY_TIMES) && (mudstate.sql_socket == -1)) { sleep(1); sql_init(); retries++; } if (mudstate.sql_socket != -1) got_rows = msqlQuery(mudstate.sql_socket, q_string); } if (got_rows == -1) { notify(player, msqlErrMsg); if (buff) safe_str("#-1", buff, bufc); return -1; } /* A null store means that this wasn't a SELECT */ qres = msqlStoreResult(); if (!qres) { notify(player, tprintf("SQL query touched %d %s.", got_rows, (got_rows == 1) ? "row" : "rows")); return 0; } /* Check to make sure we got rows back. */ got_rows = msqlNumRows(qres); if (got_rows == 0) { return 0; } /* Construct properly-delimited data. */ if (buff) { for (i = 0; i < got_rows; i++) { if (i > 0) { print_sep(row_delim, buff, bufc); } row_p = msqlFetchRow(qres); if (row_p) { got_fields = msqlNumFields(qres); for (j = 0; j < got_fields; j++) { if (j > 0) { print_sep(field_delim, buff, bufc); } if (row_p[j] && *row_p[j]) safe_str(row_p[j], buff, bufc); } } } } else { for (i = 0; i < got_rows; i++) { row_p = msqlFetchRow(qres); if (row_p) { got_fields = msqlNumFields(qres); for (j = 0; j < got_fields; j++) { if (row_p[j] && *row_p[j]) { notify(player, tprintf("Row %d, Field %d: %s", i+1, j+1, row_p[j])); } else { notify(player, tprintf("Row %d, Field %d: NULL", i+1, j+1)); } } } else { notify(player, tprintf("Row %d: NULL", i+1)); } } } msqlFreeResult(qres); return 0; }