/* -*- LPC -*- */
/*
* $Locker: $
* $Id: mysql.c,v 1.15 2003/02/19 21:02:07 pinkfish Exp $
* $Log: mysql.c,v $
* Revision 1.15 2003/02/19 21:02:07 pinkfish
* Make better end of packet detection things.
*
* Revision 1.14 2003/02/07 02:46:03 pinkfish
* Fix up some more issues with mysql and newlines.
*
* Revision 1.13 2003/02/07 02:07:08 pinkfish
* Fix up some errors in how it handles the return of sql queries.
*
* Revision 1.12 2001/07/26 18:17:57 pinkfish
* Working fine.
*
* Revision 1.11 2001/03/13 04:37:56 ceres
* Fixed runtime
*
* Revision 1.10 2001/03/13 01:03:19 ceres
* Fixed runtime
*
* Revision 1.9 2001/03/11 18:33:38 pinkfish
* Fix p a runtime that was causing errors in the sql handling.
*
* Revision 1.8 2001/03/09 21:15:00 pinkfish
* Fix up the fix.
*
* Revision 1.7 2001/03/09 21:01:14 pinkfish
* Fix up issues with two many sql reuqests in a row.
*
* Revision 1.6 2001/03/09 20:51:51 pinkfish
* Add in some testing stuff.
*
* Revision 1.5 2001/03/07 20:16:15 pinkfish
* Add in an example and fix some small issues.
*
* Revision 1.4 2000/07/14 03:54:50 pinkfish
* Change it to work over multiple chunk sends.
*
* Revision 1.3 2000/07/14 03:10:42 pinkfish
* Add in some code to do debugging.
*
* Revision 1.2 2000/06/21 20:37:17 pinkfish
* Fix up a biunch of things.
*
* Revision 1.1 2000/06/20 05:10:02 pinkfish
* Initial revision
*
* Revision 1.1 1998/01/06 05:17:08 ceres
* Initial revision
*
*/
/**
* This object handles all the mysql requests for the system.
* It connects to an external python script todo the actual mysql processing,
* it is done this way to make the sql requests non-blocking so they do not
* slow down the mud.
* @author Pinkfish
* @started Tue Jun 20 14:29:01 PDT 2000
*/
#include <network.h>
inherit CLIENT;
#include <db.h>
private int _fd;
private int _currentId;
private int _callout_id;
private mapping _requests;
private string data;
protected void setup() {
int id;
function func;
_fd = eventCreateSocket("127.0.0.1", 7865);
if (_fd < 0) {
printf("Bad fd.\n");
remove_call_out(_callout_id);
_callout_id = call_out("setup", 10);
//
// Send back an error to anyone still waiting.
//
foreach (id, func in _requests) {
map_delete(_requests, id);
evaluate(func, DB_ERROR_BAD_SOCKET, socket_error(_fd));
}
}
} /* setup() */
void create() {
::create();
_fd = -1;
_requests = ([ ]);
data = "";
setup();
} /* create() */
private void locate_value(string req, int newline) {
mixed junk;
function fluff;
int index;
int new_index;
string test_data;
string check_data;
if(!req)
return;
test_data = data + req;
index = -1;
do {
new_index = strsrch(test_data[index + 1..], "SQL_END_QUERY");
//tell_creator("pinkfish", "Chunk (%O %O %O)\n", test_data[index+1..50], new_index, index);
// Not found the end marker.
if (new_index == -1) {
if (newline) {
data += req + "\n";
} else {
data += req;
}
return ;
}
index += new_index + 1;
} while (test_data[index - 1] == '+');
check_data = test_data[0..index - 1];
data = test_data[index + 13..];
//tell_creator("pinkfish", "Data: %O %O\n", check_data, data);
catch(junk = restore_variable(check_data));
if (pointerp(junk) && sizeof(junk)) {
//tell_creator("pinkfish", "Resolved...\n");
fluff = _requests[junk[0]];
map_delete(_requests, junk[0]);
if (fluff) {
//tell_creator("pinkfish", "%O: %O %O\n", data + req, junk, fluff);
evaluate(fluff, junk[1], junk[2]);
}
}
} /* locate_value() */
/** @ignore yes */
void eventRead(int fd, string str) {
string req;
string* bits;
if (strlen(str) == 1 && str[<1] == '\n') {
locate_value("", 1);
return ;
}
bits = explode(str, "\n");
//tell_creator("pinkfish", "Stuff (%O)\n", str);
foreach (req in bits[0..<2]) {
locate_value(req, 1);
}
locate_value(bits[<1], 0);
//data += bits[<1];
//if (str[<1] == '\n') {
//locate_value("");
//}
} /* eventRead() */
/** @ignore yes */
protected void eventNewConnection(int fd) {
} /* eventNewConnection() */
/** @ignore yes */
protected void eventSocketClosed(int fd) {
_fd = -1;
remove_call_out(_callout_id);
_callout_id = call_out("setup", 10);
} /* eventSocketClosed() */
/** @ignore yes */
protected void eventSocketError(string str, int x) {
//tell_creator("pinkfish", str + " -- " + socket_error(x) + " -- " + x + "\n");
_fd = -1;
remove_call_out(_callout_id);
_callout_id = call_out("setup", 10);
}
/** @ignore yes */
protected void eventSocketClose(int fd) {
_fd = -1;
remove_call_out(_callout_id);
_callout_id = call_out("setup", 10);
} /* eventSocketClose() */
/**
* This is the method you call to make an sql request. You pass in the
* database, user and password you wish to use to connect to the
* sql sever. You also pass in the request you wish to make and the call
* back function to call.
* <p>
* The call back function will be passed two arguements, they are a
* 'type' and a 'data' arguement. The type will always be an integer and
* it registers the success or failure of the database query. The
* data will either be a string (in the case of an error) or it will be
* teh returned data (in the case of request). The returned data will
* be an array of mappings, the mappings contain keys of the field type
* and the data value being the returned data.
* <p>
* The format of the function call is:<br>
* void return_function(int status, mapping data)
* @example
* void finish_request(int type, mixed* data, object person) {
* string ret;
* mapping row;
*
* if (type == DB_SUCCESS) {
* ret = "";
* foreach (row in data) {
* ret += sprintf("%-15s %s\n", row["Fixer"], "" + row["bing"]);
* }
* } else {
* ret = "Some sort of horrible error!\n";
* }
* person->more_string(ret, "details");
* }
*
* DB_HANDLER->make_sql_request("errors", CONFIG_DB_USER, "", request,
* (: finish_request($1, $2, $(this_player())) :));
* @param db the database to connect to
* @param user the user to use
* @param pass the password to use
* @param request the request to make
* @param finish the call back function
*/
void make_sql_request(string db, string user, string pass, string request,
function finish) {
mixed *stuff;
if (!functionp(finish)) {
printf("finish arguement to make_sql_request is not a function.\n");
return ;
}
_requests[_currentId] = finish;
if (_fd == -1) {
setup();
}
if (_fd == -1) {
return ;
}
stuff = ({ _currentId++, user, pass, db, request });
eventWrite(_fd, save_variable(stuff) + "\n");
} /* make_sql_request() */
/**
* This will do exciting things when we dest.
* @ignore yes
*/
void dest_me() {
int id;
function func;
foreach (id, func in _requests) {
map_delete(_requests, id);
catch(evaluate(func, DB_ERROR_BAD_SOCKET, "the server was dested."));
}
::dest_me();
} /* dest_me() */
/** @ignore yes */
mapping query_requests() {
return copy(_requests);
} /* query_requests() */