289 lines
6.5 KiB
C++
289 lines
6.5 KiB
C++
|
/**
|
||
|
* vim: set ts=4 :
|
||
|
* =============================================================================
|
||
|
* SourceMod MySQL Extension
|
||
|
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||
|
* =============================================================================
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify it under
|
||
|
* the terms of the GNU General Public License, version 3.0, as published by the
|
||
|
* Free Software Foundation.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||
|
* details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License along with
|
||
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*
|
||
|
* As a special exception, AlliedModders LLC gives you permission to link the
|
||
|
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||
|
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||
|
* by the Valve Corporation. You must obey the GNU General Public License in
|
||
|
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||
|
* this exception to all derivative works. AlliedModders LLC defines further
|
||
|
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||
|
* or <http://www.sourcemod.net/license.php>.
|
||
|
*
|
||
|
* Version: $Id$
|
||
|
*/
|
||
|
|
||
|
#include "MyStatement.h"
|
||
|
#include "MyBoundResults.h"
|
||
|
|
||
|
MyStatement::MyStatement(MyDatabase *db, MYSQL_STMT *stmt)
|
||
|
: m_mysql(db->m_mysql), m_pParent(db), m_stmt(stmt), m_rs(NULL), m_Results(false)
|
||
|
{
|
||
|
m_Params = (unsigned int)mysql_stmt_param_count(m_stmt);
|
||
|
|
||
|
if (m_Params)
|
||
|
{
|
||
|
m_pushinfo = (ParamBind *)malloc(sizeof(ParamBind) * m_Params);
|
||
|
memset(m_pushinfo, 0, sizeof(ParamBind) * m_Params);
|
||
|
m_bind = (MYSQL_BIND *)malloc(sizeof(MYSQL_BIND) * m_Params);
|
||
|
memset(m_bind, 0, sizeof(MYSQL_BIND) * m_Params);
|
||
|
} else {
|
||
|
m_pushinfo = NULL;
|
||
|
m_bind = NULL;
|
||
|
}
|
||
|
|
||
|
m_pParent->IncReferenceCount();
|
||
|
|
||
|
m_pRes = mysql_stmt_result_metadata(stmt);
|
||
|
m_Results = false;
|
||
|
}
|
||
|
|
||
|
MyStatement::~MyStatement()
|
||
|
{
|
||
|
/* Free result set structures */
|
||
|
delete m_rs;
|
||
|
|
||
|
/* Free old blobs */
|
||
|
for (unsigned int i=0; i<m_Params; i++)
|
||
|
{
|
||
|
free(m_pushinfo[i].blob);
|
||
|
}
|
||
|
|
||
|
/* Free our allocated arrays */
|
||
|
free(m_pushinfo);
|
||
|
free(m_bind);
|
||
|
|
||
|
/* Close our mysql handles */
|
||
|
if (m_pRes)
|
||
|
{
|
||
|
mysql_free_result(m_pRes);
|
||
|
}
|
||
|
mysql_stmt_close(m_stmt);
|
||
|
|
||
|
/* Tell the parent database that we're done referencing it */
|
||
|
m_pParent->Close();
|
||
|
}
|
||
|
|
||
|
void MyStatement::Destroy()
|
||
|
{
|
||
|
delete this;
|
||
|
}
|
||
|
|
||
|
bool MyStatement::FetchMoreResults()
|
||
|
{
|
||
|
/* Multiple result sets are not supported by statements,
|
||
|
* thank god.
|
||
|
*/
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void *MyStatement::CopyBlob(unsigned int param, const void *blobptr, size_t length)
|
||
|
{
|
||
|
void *copy_ptr = NULL;
|
||
|
|
||
|
if (m_pushinfo[param].blob != NULL)
|
||
|
{
|
||
|
if (m_pushinfo[param].length < length)
|
||
|
{
|
||
|
free(m_pushinfo[param].blob);
|
||
|
} else {
|
||
|
copy_ptr = m_pushinfo[param].blob;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (copy_ptr == NULL)
|
||
|
{
|
||
|
copy_ptr = malloc(length);
|
||
|
m_pushinfo[param].blob = copy_ptr;
|
||
|
m_pushinfo[param].length = length;
|
||
|
}
|
||
|
|
||
|
memcpy(copy_ptr, blobptr, length);
|
||
|
|
||
|
return copy_ptr;
|
||
|
}
|
||
|
|
||
|
bool MyStatement::BindParamInt(unsigned int param, int num, bool signd)
|
||
|
{
|
||
|
if (param >= m_Params)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_pushinfo[param].data.ival = num;
|
||
|
m_bind[param].buffer_type = MYSQL_TYPE_LONG;
|
||
|
m_bind[param].buffer = &(m_pushinfo[param].data.ival);
|
||
|
m_bind[param].is_unsigned = signd ? 0 : 1;
|
||
|
m_bind[param].length = NULL;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool MyStatement::BindParamFloat(unsigned int param, float f)
|
||
|
{
|
||
|
if (param >= m_Params)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_pushinfo[param].data.fval = f;
|
||
|
m_bind[param].buffer_type = MYSQL_TYPE_FLOAT;
|
||
|
m_bind[param].buffer = &(m_pushinfo[param].data.fval);
|
||
|
m_bind[param].length = NULL;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool MyStatement::BindParamString(unsigned int param, const char *text, bool copy)
|
||
|
{
|
||
|
if (param >= m_Params)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const void *final_ptr;
|
||
|
size_t len;
|
||
|
|
||
|
if (copy)
|
||
|
{
|
||
|
len = strlen(text);
|
||
|
final_ptr = CopyBlob(param, text, len+1);
|
||
|
} else {
|
||
|
len = strlen(text);
|
||
|
final_ptr = text;
|
||
|
}
|
||
|
|
||
|
m_bind[param].buffer_type = MYSQL_TYPE_STRING;
|
||
|
m_bind[param].buffer = (void *)final_ptr;
|
||
|
m_bind[param].buffer_length = (unsigned long)len;
|
||
|
m_bind[param].length = &(m_bind[param].buffer_length);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool MyStatement::BindParamBlob(unsigned int param, const void *data, size_t length, bool copy)
|
||
|
{
|
||
|
if (param >= m_Params)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const void *final_ptr;
|
||
|
|
||
|
if (copy)
|
||
|
{
|
||
|
final_ptr = CopyBlob(param, data, length);
|
||
|
} else {
|
||
|
final_ptr = data;
|
||
|
}
|
||
|
|
||
|
m_bind[param].buffer_type = MYSQL_TYPE_BLOB;
|
||
|
m_bind[param].buffer = (void *)final_ptr;
|
||
|
m_bind[param].buffer_length = (unsigned long)length;
|
||
|
m_bind[param].length = &(m_bind[param].buffer_length);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool MyStatement::BindParamNull(unsigned int param)
|
||
|
{
|
||
|
if (param >= m_Params)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_bind[param].buffer_type = MYSQL_TYPE_NULL;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool MyStatement::Execute()
|
||
|
{
|
||
|
/* Clear any past result first! */
|
||
|
m_Results = false;
|
||
|
|
||
|
/* Bind the parameters */
|
||
|
if (m_Params)
|
||
|
{
|
||
|
if (mysql_stmt_bind_param(m_stmt, m_bind) != 0)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (mysql_stmt_execute(m_stmt) != 0)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* Skip away if we don't have data */
|
||
|
if (!m_pRes)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/* If we don't have a result manager, create one. */
|
||
|
if (!m_rs)
|
||
|
{
|
||
|
m_rs = new MyBoundResults(m_stmt, m_pRes);
|
||
|
}
|
||
|
|
||
|
/* Tell the result set to update its bind info,
|
||
|
* and initialize itself if necessary.
|
||
|
*/
|
||
|
if (!(m_Results = m_rs->Initialize()))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* Try precaching the results. */
|
||
|
m_Results = (mysql_stmt_store_result(m_stmt) == 0);
|
||
|
|
||
|
/* Update now that the data is known. */
|
||
|
m_rs->Update();
|
||
|
|
||
|
/* Return indicator */
|
||
|
return m_Results;
|
||
|
}
|
||
|
|
||
|
const char *MyStatement::GetError(int *errCode/* =NULL */)
|
||
|
{
|
||
|
if (errCode)
|
||
|
{
|
||
|
*errCode = mysql_stmt_errno(m_stmt);
|
||
|
}
|
||
|
|
||
|
return mysql_stmt_error(m_stmt);
|
||
|
}
|
||
|
|
||
|
unsigned int MyStatement::GetAffectedRows()
|
||
|
{
|
||
|
return (unsigned int)mysql_stmt_affected_rows(m_stmt);
|
||
|
}
|
||
|
|
||
|
unsigned int MyStatement::GetInsertID()
|
||
|
{
|
||
|
return (unsigned int)mysql_stmt_insert_id(m_stmt);
|
||
|
}
|
||
|
|
||
|
IResultSet *MyStatement::GetResultSet()
|
||
|
{
|
||
|
return (m_Results ? m_rs : NULL);
|
||
|
}
|