SQL: sqlreadfield with a negative row returns field name, server request/result stacks changed to queues, query references numbers limited to +/-50000, proper library init/end calls, sqlversion call
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@2942 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
7f73771ba2
commit
9e7a43c96c
1 changed files with 180 additions and 74 deletions
|
@ -6338,6 +6338,7 @@ typedef struct queryresult_s
|
|||
struct queryresult_s *next; // next result in queue
|
||||
int rows; // rows contained in single result set
|
||||
int columns; // fields
|
||||
qboolean eof; // end of query reached
|
||||
MYSQL_RES *result; // result set from mysql
|
||||
char **resultset; // stored result set from partial fetch
|
||||
char error[1]; // error string, "" if none (struct hack)
|
||||
|
@ -6348,16 +6349,76 @@ typedef struct sqlserver_s
|
|||
void *thread; // worker thread for server
|
||||
MYSQL *mysql; // mysql server
|
||||
qboolean active; // set to false to kill thread
|
||||
void *requestlock; // mutex for linked list read/write
|
||||
void *resultlock; // mutex for linked list read/write
|
||||
void *requestlock; // mutex for queue read/write
|
||||
void *resultlock; // mutex for queue read/write
|
||||
int querynum; // next reference number for queries
|
||||
queryrequest_t *requests; // query requests
|
||||
queryresult_t *results; // query results link
|
||||
queryrequest_t *requests; // query requests queue
|
||||
queryrequest_t *requestslast; // query requests queue last link
|
||||
queryresult_t *results; // query results queue
|
||||
queryresult_t *resultslast; // query results queue last link
|
||||
queryresult_t *currentresult; // current called result
|
||||
queryresult_t *serverresult; // server error results
|
||||
char host[1]; // host (struct hack)
|
||||
} sqlserver_t;
|
||||
|
||||
void SQL_PushResult(sqlserver_t *server, queryresult_t *qres)
|
||||
{
|
||||
Sys_LockMutex(server->resultlock);
|
||||
qres->next = NULL;
|
||||
if (!server->resultslast)
|
||||
server->results = server->resultslast = qres;
|
||||
else
|
||||
server->resultslast = server->resultslast->next = qres;
|
||||
Sys_UnlockMutex(server->resultlock);
|
||||
}
|
||||
|
||||
queryresult_t *SQL_PullResult(sqlserver_t *server)
|
||||
{
|
||||
queryresult_t *qres;
|
||||
Sys_LockMutex(server->resultlock);
|
||||
qres = server->results;
|
||||
if (!qres)
|
||||
{
|
||||
Sys_UnlockMutex(server->resultlock);
|
||||
return NULL;
|
||||
}
|
||||
server->results = qres->next;
|
||||
if (!server->results)
|
||||
server->resultslast = NULL;
|
||||
Sys_UnlockMutex(server->resultlock);
|
||||
|
||||
return qres;
|
||||
}
|
||||
|
||||
void SQL_PushRequest(sqlserver_t *server, queryrequest_t *qreq)
|
||||
{
|
||||
Sys_LockMutex(server->requestlock);
|
||||
qreq->next = NULL;
|
||||
if (!server->requestslast)
|
||||
server->requests = server->requestslast = qreq;
|
||||
else
|
||||
server->requestslast = server->requestslast->next = qreq;
|
||||
Sys_UnlockMutex(server->requestlock);
|
||||
}
|
||||
|
||||
queryrequest_t *SQL_PullRequest(sqlserver_t *server)
|
||||
{
|
||||
queryrequest_t *qreq;
|
||||
Sys_LockMutex(server->requestlock);
|
||||
qreq = server->requests;
|
||||
if (!qreq)
|
||||
{
|
||||
Sys_UnlockMutex(server->requestlock);
|
||||
return NULL;
|
||||
}
|
||||
server->requests = qreq->next;
|
||||
if (!server->requests)
|
||||
server->requestslast = NULL;
|
||||
Sys_UnlockMutex(server->requestlock);
|
||||
|
||||
return qreq;
|
||||
}
|
||||
|
||||
sqlserver_t **sqlservers;
|
||||
int sqlservercount;
|
||||
|
||||
|
@ -6380,20 +6441,14 @@ int sql_serverworker(void *sref)
|
|||
server->active = false;
|
||||
|
||||
while (server->active)
|
||||
{
|
||||
queryrequest_t *qreq = NULL;
|
||||
|
||||
// replace this with a conditional
|
||||
if (server->requests)
|
||||
{
|
||||
Sys_LockMutex(server->requestlock);
|
||||
qreq = server->requests;
|
||||
server->requests = qreq->next;
|
||||
Sys_UnlockMutex(server->requestlock);
|
||||
}
|
||||
{
|
||||
// TODO: replace this with a conditional
|
||||
if (!server->requests)
|
||||
continue;
|
||||
|
||||
while (qreq)
|
||||
while (1)
|
||||
{
|
||||
queryrequest_t *qreq = NULL;
|
||||
queryresult_t *qres;
|
||||
const char *qerror = NULL;
|
||||
MYSQL_RES *mysqlres = NULL;
|
||||
|
@ -6401,6 +6456,10 @@ int sql_serverworker(void *sref)
|
|||
int columns = -1;
|
||||
int qesize = 0;
|
||||
|
||||
if (!(qreq = SQL_PullRequest(server)))
|
||||
break;
|
||||
|
||||
// perform the query and fill out the result structure
|
||||
if (mysql_query(server->mysql, qreq->query))
|
||||
qerror = mysql_error(server->mysql);
|
||||
else // query succeeded
|
||||
|
@ -6414,6 +6473,8 @@ int sql_serverworker(void *sref)
|
|||
else if (mysql_field_count(server->mysql) == 0) // no result set
|
||||
{
|
||||
rows = mysql_affected_rows(server->mysql);
|
||||
if (rows < 0)
|
||||
rows = 0;
|
||||
columns = 0;
|
||||
}
|
||||
else // error
|
||||
|
@ -6429,35 +6490,24 @@ int sql_serverworker(void *sref)
|
|||
qres->rows = rows;
|
||||
qres->columns = columns;
|
||||
qres->request = qreq;
|
||||
qres->eof = true; // store result has no more rows to read afterwards
|
||||
qreq->next = NULL;
|
||||
|
||||
Sys_LockMutex(server->resultlock);
|
||||
qres->next = server->results;
|
||||
server->results = qres;
|
||||
Sys_UnlockMutex(server->resultlock);
|
||||
|
||||
Sys_LockMutex(server->requestlock);
|
||||
qreq = server->requests;
|
||||
if (qreq)
|
||||
server->requests = qreq->next;
|
||||
Sys_UnlockMutex(server->requestlock);
|
||||
SQL_PushResult(server, qres);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if we have a server error we still need to put it on the queue
|
||||
if (error)
|
||||
{
|
||||
{
|
||||
int esize = Q_strlen(error);
|
||||
queryresult_t *qres = (queryresult_t *)Z_Malloc(sizeof(queryresult_t) + esize);
|
||||
|
||||
qres->rows = qres->columns = -1;
|
||||
Q_strncpy(qres->error, error, esize);
|
||||
|
||||
Sys_LockMutex(server->resultlock);
|
||||
qres->next = server->results;
|
||||
server->results = qres;
|
||||
Sys_UnlockMutex(server->resultlock);
|
||||
|
||||
SQL_PushResult(server, qres);
|
||||
}
|
||||
|
||||
mysql_close(server->mysql);
|
||||
|
@ -6474,6 +6524,7 @@ void PF_sqlconnect (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|||
int hsize;
|
||||
sqlserver_t *server;
|
||||
|
||||
// alloc or realloc sql servers array
|
||||
if (sqlservers == NULL)
|
||||
{
|
||||
serverref = 0;
|
||||
|
@ -6488,6 +6539,8 @@ void PF_sqlconnect (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|||
}
|
||||
|
||||
// TODO: need option logic for "default server" here
|
||||
|
||||
// assemble server structure
|
||||
hoststr = PR_GetStringOfs(prinst, OFS_PARM0);
|
||||
hsize = Q_strlen(hoststr);
|
||||
|
||||
|
@ -6534,20 +6587,34 @@ void PF_sqlopenquery (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|||
|
||||
qreq->callback = callfunc;
|
||||
querynum = qreq->num = sqlservers[serverref]->querynum;
|
||||
sqlservers[serverref]->querynum++;
|
||||
// prevent the reference num from getting too big to prevent FP problems
|
||||
if (++sqlservers[serverref]->querynum > 50000)
|
||||
sqlservers[serverref]->querynum -= 100000;
|
||||
|
||||
Q_strncpy(qreq->query, querystr, qsize);
|
||||
|
||||
// add into request queue
|
||||
Sys_LockMutex(sqlservers[serverref]->requestlock);
|
||||
qreq->next = sqlservers[serverref]->requests;
|
||||
sqlservers[serverref]->requests = qreq;
|
||||
Sys_UnlockMutex(sqlservers[serverref]->requestlock);
|
||||
SQL_PushRequest(sqlservers[serverref], qreq);
|
||||
|
||||
// TODO: conditional trip here
|
||||
|
||||
G_FLOAT(OFS_RETURN) = querynum;
|
||||
}
|
||||
|
||||
void SQL_CloseCurrentQuery(sqlserver_t *server)
|
||||
{
|
||||
if (!server->currentresult)
|
||||
return;
|
||||
|
||||
// deallocate current result
|
||||
if (server->currentresult->result)
|
||||
mysql_free_result(server->currentresult->result);
|
||||
|
||||
if (server->currentresult->request)
|
||||
Z_Free(server->currentresult->request);
|
||||
Z_Free(server->currentresult);
|
||||
server->currentresult = NULL;
|
||||
}
|
||||
|
||||
void PF_sqlclosequery (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
||||
{
|
||||
int serverref = G_FLOAT(OFS_PARM0);
|
||||
|
@ -6559,17 +6626,12 @@ void PF_sqlclosequery (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|||
!sqlservers[serverref]->currentresult ||
|
||||
!sqlservers[serverref]->currentresult->request ||
|
||||
sqlservers[serverref]->currentresult->request->num != queryref)
|
||||
return;
|
||||
return; // close query isn't for current query
|
||||
|
||||
// TODO: partial resultset logic not implemented yet
|
||||
// TODO: should we allow closing queries out of scope?
|
||||
|
||||
// deallocate current result
|
||||
if (sqlservers[serverref]->currentresult->result)
|
||||
mysql_free_result(sqlservers[serverref]->currentresult->result);
|
||||
|
||||
if (sqlservers[serverref]->currentresult->request)
|
||||
Z_Free(sqlservers[serverref]->currentresult->request);
|
||||
Z_Free(sqlservers[serverref]->currentresult);
|
||||
sqlservers[serverref]->currentresult = NULL;
|
||||
SQL_CloseCurrentQuery(sqlservers[serverref]);
|
||||
}
|
||||
|
||||
void PF_sqlreadfield (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
||||
|
@ -6597,24 +6659,42 @@ void PF_sqlreadfield (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|||
}
|
||||
else
|
||||
{ // store_result query
|
||||
MYSQL_ROW sqlrow;
|
||||
|
||||
if (sqlservers[serverref]->currentresult->rows < row ||
|
||||
sqlservers[serverref]->currentresult->columns < col)
|
||||
sqlservers[serverref]->currentresult->columns < col ||
|
||||
col < 0)
|
||||
{ // out of bounds
|
||||
G_INT(OFS_RETURN) = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
mysql_data_seek(sqlservers[serverref]->currentresult->result, row);
|
||||
sqlrow = mysql_fetch_row(sqlservers[serverref]->currentresult->result);
|
||||
if (!sqlrow || !sqlrow[col])
|
||||
{
|
||||
G_INT(OFS_RETURN) = 0;
|
||||
return;
|
||||
if (row < 0)
|
||||
{ // fetch field name
|
||||
MYSQL_FIELD *field;
|
||||
|
||||
field = mysql_fetch_field_direct(sqlservers[serverref]->currentresult->result, col);
|
||||
|
||||
if (!field)
|
||||
{
|
||||
G_INT(OFS_RETURN) = 0;
|
||||
return;
|
||||
}
|
||||
else
|
||||
RETURN_TSTRING(field->name);
|
||||
}
|
||||
else
|
||||
RETURN_TSTRING(sqlrow[col]);
|
||||
{ // fetch data
|
||||
MYSQL_ROW sqlrow;
|
||||
|
||||
mysql_data_seek(sqlservers[serverref]->currentresult->result, row);
|
||||
sqlrow = mysql_fetch_row(sqlservers[serverref]->currentresult->result);
|
||||
if (!sqlrow || !sqlrow[col])
|
||||
{
|
||||
G_INT(OFS_RETURN) = 0;
|
||||
return;
|
||||
}
|
||||
else
|
||||
RETURN_TSTRING(sqlrow[col]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6623,20 +6703,20 @@ void PF_sqlerror (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|||
int serverref = G_FLOAT(OFS_PARM0);
|
||||
|
||||
if (serverref < 0 || serverref >= sqlservercount)
|
||||
{
|
||||
{ // invalid server reference
|
||||
RETURN_TSTRING("");
|
||||
return;
|
||||
}
|
||||
|
||||
if (*svprogfuncs->callargc == 2)
|
||||
{
|
||||
{ // query-specific error request
|
||||
int queryref = G_FLOAT(OFS_PARM1);
|
||||
|
||||
if (!sqlservers[serverref]->active ||
|
||||
!sqlservers[serverref]->currentresult ||
|
||||
!sqlservers[serverref]->currentresult->request ||
|
||||
sqlservers[serverref]->currentresult->request->num != queryref)
|
||||
{
|
||||
{ // invalid query
|
||||
RETURN_TSTRING("");
|
||||
return;
|
||||
}
|
||||
|
@ -6644,9 +6724,9 @@ void PF_sqlerror (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|||
RETURN_TSTRING(sqlservers[serverref]->currentresult->error);
|
||||
}
|
||||
else
|
||||
{
|
||||
{ // server-specific error request
|
||||
if (!sqlservers[serverref]->serverresult)
|
||||
{
|
||||
{ // no error result on server, return empty string
|
||||
RETURN_TSTRING("");
|
||||
return;
|
||||
}
|
||||
|
@ -6662,8 +6742,8 @@ void PF_sqlescape (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|||
|
||||
toescape = PR_GetStringOfs(prinst, OFS_PARM1);
|
||||
|
||||
if (!*toescape || serverref < 0 || serverref >= sqlservercount || sqlservers[serverref]->active == false)
|
||||
{
|
||||
if (!toescape || !*toescape || serverref < 0 || serverref >= sqlservercount || sqlservers[serverref]->active == false)
|
||||
{ // invalid string or server reference
|
||||
RETURN_TSTRING("");
|
||||
return;
|
||||
}
|
||||
|
@ -6673,6 +6753,20 @@ void PF_sqlescape (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|||
RETURN_TSTRING(escaped);
|
||||
}
|
||||
|
||||
void PF_sqlversion (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
||||
{
|
||||
int serverref = G_FLOAT(OFS_PARM0);
|
||||
|
||||
if (serverref < 0 || serverref >= sqlservercount || sqlservers[serverref]->active == false)
|
||||
{ // invalid string or server reference
|
||||
RETURN_TSTRING("");
|
||||
return;
|
||||
}
|
||||
|
||||
// no other sql backends yet so just report mysql string for any active
|
||||
RETURN_TSTRING(va("mysql: %s", mysql_get_client_info()));
|
||||
}
|
||||
|
||||
void SQL_Cycle (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
||||
{
|
||||
int i;
|
||||
|
@ -6684,15 +6778,9 @@ void SQL_Cycle (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|||
|
||||
while (1)
|
||||
{
|
||||
Sys_LockMutex(server->resultlock);
|
||||
qres = server->results;
|
||||
if (!qres)
|
||||
{
|
||||
Sys_UnlockMutex(server->resultlock);
|
||||
// get a result off of queue
|
||||
if (!(qres = SQL_PullResult(server)))
|
||||
break;
|
||||
}
|
||||
server->results = qres->next;
|
||||
Sys_UnlockMutex(server->resultlock);
|
||||
|
||||
qres->next = NULL;
|
||||
if (qres->request && qres->request->callback)
|
||||
|
@ -6704,8 +6792,15 @@ void SQL_Cycle (progfuncs_t *prinst, struct globalvars_s *pr_globals)
|
|||
G_FLOAT(OFS_PARM1) = qres->request->num;
|
||||
G_FLOAT(OFS_PARM2) = qres->rows;
|
||||
G_FLOAT(OFS_PARM3) = qres->columns;
|
||||
G_FLOAT(OFS_PARM4) = qres->eof;
|
||||
|
||||
PR_ExecuteProgram(prinst, qres->request->callback);
|
||||
|
||||
if (qres->eof && server->currentresult)
|
||||
{ // TODO: is this even worth complaining about?
|
||||
Con_Printf("QC didn't close query %i: %s\n", qres->request->num, qres->request->query);
|
||||
SQL_CloseCurrentQuery(server);
|
||||
}
|
||||
}
|
||||
}
|
||||
else // error or server-only result
|
||||
|
@ -6746,6 +6841,12 @@ void SQL_Init()
|
|||
Con_Printf("MYSQL client is not thread safe!\n");
|
||||
// TODO: disable extension here
|
||||
}
|
||||
|
||||
if (!mysql_library_init(0, NULL, NULL))
|
||||
{
|
||||
Con_Printf("MYSQL library init failed!\n");
|
||||
// TODO: disable extension here
|
||||
}
|
||||
}
|
||||
|
||||
void SQL_DeInit()
|
||||
|
@ -6788,6 +6889,8 @@ void SQL_DeInit()
|
|||
Z_Free(qres);
|
||||
}
|
||||
|
||||
SQL_CloseCurrentQuery(server);
|
||||
|
||||
if (server->serverresult)
|
||||
Z_Free(server->serverresult);
|
||||
|
||||
|
@ -6798,6 +6901,8 @@ void SQL_DeInit()
|
|||
sqlservers = NULL;
|
||||
sqlservercount = 0;
|
||||
|
||||
mysql_library_end();
|
||||
|
||||
mysql_dll_close();
|
||||
}
|
||||
#endif
|
||||
|
@ -6933,7 +7038,7 @@ lh_extension_t QSG_Extensions[] = {
|
|||
|
||||
#ifdef SQL
|
||||
// serverside SQL functions for managing an SQL database connection
|
||||
{"FTE_SQL", 7, NULL, {"sqlconnect","sqldisconnect","sqlopenquery","sqlclosequery","sqlreadfield","sqlerror","sqlescape"}},
|
||||
{"FTE_SQL", 8, NULL, {"sqlconnect","sqldisconnect","sqlopenquery","sqlclosequery","sqlreadfield","sqlerror","sqlescape","sqlversion"}},
|
||||
#endif
|
||||
//eperimental advanced strings functions.
|
||||
//reuses the FRIK_FILE builtins (with substring extension)
|
||||
|
@ -10180,11 +10285,12 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
|
|||
#ifdef SQL
|
||||
{"sqlconnect", PF_sqlconnect, 0, 0, 0, 250}, // #250 float([string server]) sqlconnect (FTE_SQL)
|
||||
{"sqldisconnect", PF_sqldisconnect, 0, 0, 0, 251}, // #251 void(float serveridx) sqldisconnect (FTE_SQL)
|
||||
{"sqlopenquery", PF_sqlopenquery, 0, 0, 0, 252}, // #252 float(float serveridx, string query, void(float serveridx, float queryidx, float rows, float columns) callback) sqlopenquery (FTE_SQL)
|
||||
{"sqlopenquery", PF_sqlopenquery, 0, 0, 0, 252}, // #252 float(float serveridx, string query, void(float serveridx, float queryidx, float rows, float columns, float eof) callback) sqlopenquery (FTE_SQL)
|
||||
{"sqlclosequery", PF_sqlclosequery, 0, 0, 0, 253}, // #253 void(float serveridx, float queryidx) sqlclosequery (FTE_SQL)
|
||||
{"sqlreadfield", PF_sqlreadfield, 0, 0, 0, 254}, // #254 string(float serveridx, float queryidx, float row, float column) sqlreadfield (FTE_SQL)
|
||||
{"sqlerror", PF_sqlerror, 0, 0, 0, 255}, // #255 string(float serveridx, [float queryidx]) sqlerror (FTE_SQL)
|
||||
{"sqlescape", PF_sqlescape, 0, 0, 0, 256}, // #256 string(float serveridx, string data) sqlescape (FTE_SQL)
|
||||
{"sqlversion", PF_sqlversion, 0, 0, 0, 257}, // #257 string(float serveridx) sqlversion (FTE_SQL)
|
||||
#endif
|
||||
|
||||
//EXT_CSQC
|
||||
|
|
Loading…
Reference in a new issue