tinymush-3.1p1/game/backups/
tinymush-3.1p1/game/bin/
tinymush-3.1p1/game/data/
tinymush-3.1p1/game/modules/
tinymush-3.1p1/game/modules/old/
tinymush-3.1p1/src/modules/comsys/
tinymush-3.1p1/src/modules/hello/
tinymush-3.1p1/src/modules/mail/
tinymush-3.1p1/src/tools/
/* db_msql.c - Implements accessing an mSQL 2.x database. */
/* $Id: db_msql.c,v 1.15 2003/02/24 18:05:22 rmg Exp $ */

#include "copyright.h"
#include "autoconf.h"
#include "config.h"

#include "alloc.h"	/* required by mudconf */
#include "flags.h"	/* required by mudconf */
#include "htab.h"	/* required by mudconf */
#include "mudconf.h"	/* required by code */

#include "db.h"		/* required by externs */
#include "externs.h"	/* required by code */
#include "functions.h"  /* required by code */

#include "msql.h"	/* required by code */

/* 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")
	    log_printf("Failed connection to SQL server %s: %s",
		       mudconf.sql_host, msqlErrMsg);
	ENDLOG
	return -1;
    }

    STARTLOG(LOG_ALWAYS, "SQL", "CONN")
	log_printf("Connected to SQL server %s, socket fd %d",
		   mudconf.sql_host, 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_printf("Failed db select: %s", msqlErrMsg);
	ENDLOG
	msqlClose(mudstate.sql_socket);
	mudstate.sql_socket = -1;
	return -1;
    }

    STARTLOG(LOG_ALWAYS, "SQL", "CONN")
	log_printf("SQL database selected: %s", mudconf.sql_db);
    ENDLOG
    return (mudstate.sql_socket);
}

int sql_query(player, q_string, buff, bufc, row_delim, field_delim)
    dbref player;
    char *q_string;
    char *buff;
    char **bufc;
    const Delim *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_printf("Connection died to SQL server on fd %d",
		       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;
}