added new (somewhat experimental) mysql extension and finalized the API
--HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40868
This commit is contained in:
parent
1dffe2e648
commit
11470d81ca
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "Database.h"
|
#include "Database.h"
|
||||||
#include "HandleSys.h"
|
#include "HandleSys.h"
|
||||||
|
#include "ShareSys.h"
|
||||||
#include "sourcemod.h"
|
#include "sourcemod.h"
|
||||||
|
|
||||||
DBManager g_DBMan;
|
DBManager g_DBMan;
|
||||||
@ -28,6 +29,8 @@ void DBManager::OnSourceModAllInitialized()
|
|||||||
|
|
||||||
m_DriverType = g_HandleSys.CreateType("IDriver", this, 0, NULL, &sec, g_pCoreIdent, NULL);
|
m_DriverType = g_HandleSys.CreateType("IDriver", this, 0, NULL, &sec, g_pCoreIdent, NULL);
|
||||||
m_DatabaseType = g_HandleSys.CreateType("IDatabase", this, 0, NULL, NULL, g_pCoreIdent, NULL);
|
m_DatabaseType = g_HandleSys.CreateType("IDatabase", this, 0, NULL, NULL, g_pCoreIdent, NULL);
|
||||||
|
|
||||||
|
g_ShareSys.AddInterface(NULL, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DBManager::OnSourceModShutdown()
|
void DBManager::OnSourceModShutdown()
|
||||||
|
87
extensions/mysql/Makefile
Normal file
87
extensions/mysql/Makefile
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#(C)2004-2006 SourceMM Development Team
|
||||||
|
# Makefile written by David "BAILOPAN" Anderson
|
||||||
|
|
||||||
|
SMSDK = ../..
|
||||||
|
SRCDS = ~/srcds
|
||||||
|
SOURCEMM = ../../../../sourcemm
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
### EDIT BELOW FOR OTHER PROJECTS ###
|
||||||
|
#####################################
|
||||||
|
|
||||||
|
PROJECT = sample
|
||||||
|
|
||||||
|
#Uncomment for SourceMM-enabled extensions
|
||||||
|
#LINK_HL2 = $(HL2LIB)/tier1_i486.a vstdlib_i486.so tier0_i486.so
|
||||||
|
|
||||||
|
OBJECTS = sdk/smsdk_ext.cpp extension.cpp
|
||||||
|
|
||||||
|
##############################################
|
||||||
|
### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ###
|
||||||
|
##############################################
|
||||||
|
|
||||||
|
C_OPT_FLAGS = -O3 -funroll-loops -s -pipe -fno-strict-aliasing
|
||||||
|
C_DEBUG_FLAGS = -g -ggdb3
|
||||||
|
CPP_GCC4_FLAGS = -fvisibility=hidden -fvisibility-inlines-hidden
|
||||||
|
CPP = gcc-4.1
|
||||||
|
|
||||||
|
HL2PUB = $(HL2SDK)/public
|
||||||
|
HL2LIB = $(HL2SDK)/linux_sdk
|
||||||
|
HL2SDK = $(SOURCEMM)/hl2sdk
|
||||||
|
SMM_TRUNK = $(SOURCEMM)/trunk
|
||||||
|
|
||||||
|
LINK = $(LINK_HL2) -static-libgcc
|
||||||
|
|
||||||
|
INCLUDE = -I. -I.. -Isdk -I$(HL2PUB) -I$(HL2PUB)/dlls -I$(HL2PUB)/engine -I$(HL2PUB)/tier0 -I$(HL2PUB)/tier1 \
|
||||||
|
-I$(HL2PUB)/vstdlib -I$(HL2SDK)/tier1 -I$(SMM_TRUNK) -I$(SMM_TRUNK)/sourcehook -I$(SMM_TRUNK)/sourcemm \
|
||||||
|
-I$(SMSDK)/public -I$(SMSDK)/public/sourcepawn -I$(SMSDK)/public/extensions \
|
||||||
|
|
||||||
|
CFLAGS = -D_LINUX -DNDEBUG -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -Werror -fPIC -msse -DSOURCEMOD_BUILD -DHAVE_STDINT_H
|
||||||
|
CPPFLAGS = -Wno-non-virtual-dtor -fno-exceptions -fno-rtti
|
||||||
|
|
||||||
|
################################################
|
||||||
|
### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ###
|
||||||
|
################################################
|
||||||
|
|
||||||
|
ifeq "$(DEBUG)" "true"
|
||||||
|
BIN_DIR = Debug
|
||||||
|
CFLAGS += $(C_DEBUG_FLAGS)
|
||||||
|
else
|
||||||
|
BIN_DIR = Release
|
||||||
|
CFLAGS += $(C_OPT_FLAGS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
GCC_VERSION := $(shell $(CPP) -dumpversion >&1 | cut -b1)
|
||||||
|
ifeq "$(GCC_VERSION)" "4"
|
||||||
|
CPPFLAGS += $(CPP_GCC4_FLAGS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
BINARY = $(PROJECT).ext.so
|
||||||
|
|
||||||
|
OBJ_LINUX := $(OBJECTS:%.cpp=$(BIN_DIR)/%.o)
|
||||||
|
|
||||||
|
$(BIN_DIR)/%.o: %.cpp
|
||||||
|
$(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
|
||||||
|
|
||||||
|
all:
|
||||||
|
mkdir -p $(BIN_DIR)/sdk
|
||||||
|
ln -sf $(SRCDS)/bin/vstdlib_i486.so vstdlib_i486.so
|
||||||
|
ln -sf $(SRCDS)/bin/tier0_i486.so tier0_i486.so
|
||||||
|
$(MAKE) extension
|
||||||
|
|
||||||
|
extension: $(OBJ_LINUX)
|
||||||
|
$(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) $(OBJ_LINUX) $(LINK) -shared -ldl -lm -o$(BIN_DIR)/$(BINARY)
|
||||||
|
|
||||||
|
debug:
|
||||||
|
$(MAKE) all DEBUG=true
|
||||||
|
|
||||||
|
default: all
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf Release/*.o
|
||||||
|
rm -rf Release/sdk/*.o
|
||||||
|
rm -rf Release/$(BINARY)
|
||||||
|
rm -rf Debug/*.o
|
||||||
|
rm -rf Debug/sdk/*.o
|
||||||
|
rm -rf Debug/$(BINARY)
|
48
extensions/mysql/extension.cpp
Normal file
48
extensions/mysql/extension.cpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* vim: set ts=4 :
|
||||||
|
* ===============================================================
|
||||||
|
* Sample SourceMod Extension
|
||||||
|
* Copyright (C) 2004-2007 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
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
* 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* Version: $Id: extension.cpp 763 2007-05-09 05:20:03Z damagedsoul $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "extension.h"
|
||||||
|
#include "mysql/MyDriver.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file extension.cpp
|
||||||
|
* @brief Implement extension code here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
DBI_MySQL g_MySqlDBI; /**< Global singleton for extension's main interface */
|
||||||
|
|
||||||
|
SMEXT_LINK(&g_MySqlDBI);
|
||||||
|
|
||||||
|
bool DBI_MySQL::SDK_OnLoad(char *error, size_t maxlength, bool late)
|
||||||
|
{
|
||||||
|
dbi->AddDriver(&g_MyDriver);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DBI_MySQL::SDK_OnUnload()
|
||||||
|
{
|
||||||
|
dbi->RemoveDriver(&g_MyDriver);
|
||||||
|
}
|
111
extensions/mysql/extension.h
Normal file
111
extensions/mysql/extension.h
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/**
|
||||||
|
* vim: set ts=4 :
|
||||||
|
* ===============================================================
|
||||||
|
* Sample SourceMod Extension
|
||||||
|
* Copyright (C) 2004-2007 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
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
* 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* Version: $Id: extension.h 763 2007-05-09 05:20:03Z damagedsoul $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
|
||||||
|
#define _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file extension.h
|
||||||
|
* @brief Sample extension code header.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "smsdk_ext.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sample implementation of the SDK Extension.
|
||||||
|
* Note: Uncomment one of the pre-defined virtual functions in order to use it.
|
||||||
|
*/
|
||||||
|
class DBI_MySQL : public SDKExtension
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief This is called after the initial loading sequence has been processed.
|
||||||
|
*
|
||||||
|
* @param error Error message buffer.
|
||||||
|
* @param maxlength Size of error message buffer.
|
||||||
|
* @param late Whether or not the module was loaded after map load.
|
||||||
|
* @return True to succeed loading, false to fail.
|
||||||
|
*/
|
||||||
|
virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is called right before the extension is unloaded.
|
||||||
|
*/
|
||||||
|
virtual void SDK_OnUnload();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is called once all known extensions have been loaded.
|
||||||
|
* Note: It is is a good idea to add natives here, if any are provided.
|
||||||
|
*/
|
||||||
|
//virtual void SDK_OnAllLoaded();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called when the pause state is changed.
|
||||||
|
*/
|
||||||
|
//virtual void SDK_OnPauseChange(bool paused);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief this is called when Core wants to know if your extension is working.
|
||||||
|
*
|
||||||
|
* @param error Error message buffer.
|
||||||
|
* @param maxlength Size of error message buffer.
|
||||||
|
* @return True if working, false otherwise.
|
||||||
|
*/
|
||||||
|
//virtual bool QueryRunning(char *error, size_t maxlength);
|
||||||
|
public:
|
||||||
|
#if defined SMEXT_CONF_METAMOD
|
||||||
|
/**
|
||||||
|
* @brief Called when Metamod is attached, before the extension version is called.
|
||||||
|
*
|
||||||
|
* @param error Error buffer.
|
||||||
|
* @param maxlength Maximum size of error buffer.
|
||||||
|
* @param late Whether or not Metamod considers this a late load.
|
||||||
|
* @return True to succeed, false to fail.
|
||||||
|
*/
|
||||||
|
//virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called when Metamod is detaching, after the extension version is called.
|
||||||
|
* NOTE: By default this is blocked unless sent from SourceMod.
|
||||||
|
*
|
||||||
|
* @param error Error buffer.
|
||||||
|
* @param maxlength Maximum size of error buffer.
|
||||||
|
* @return True to succeed, false to fail.
|
||||||
|
*/
|
||||||
|
//virtual bool SDK_OnMetamodUnload(char *error, size_t maxlength);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called when Metamod's pause state is changing.
|
||||||
|
* NOTE: By default this is blocked unless sent from SourceMod.
|
||||||
|
*
|
||||||
|
* @param paused Pause state being set.
|
||||||
|
* @param error Error buffer.
|
||||||
|
* @param maxlength Maximum size of error buffer.
|
||||||
|
* @return True to succeed, false to fail.
|
||||||
|
*/
|
||||||
|
//virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
|
20
extensions/mysql/msvc8/sm_mysql.sln
Normal file
20
extensions/mysql/msvc8/sm_mysql.sln
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||||
|
# Visual Studio 2005
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sm_mysql", "sm_mysql.vcproj", "{B3E797CF-4E77-4C9D-B8A8-7589B6902206}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Win32 = Debug|Win32
|
||||||
|
Release|Win32 = Release|Win32
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||||
|
{B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug|Win32.Build.0 = Debug|Win32
|
||||||
|
{B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release|Win32.ActiveCfg = Release|Win32
|
||||||
|
{B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release|Win32.Build.0 = Release|Win32
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
279
extensions/mysql/msvc8/sm_mysql.vcproj
Normal file
279
extensions/mysql/msvc8/sm_mysql.vcproj
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
<?xml version="1.0" encoding="Windows-1252"?>
|
||||||
|
<VisualStudioProject
|
||||||
|
ProjectType="Visual C++"
|
||||||
|
Version="8.00"
|
||||||
|
Name="sm_mysql"
|
||||||
|
ProjectGUID="{B3E797CF-4E77-4C9D-B8A8-7589B6902206}"
|
||||||
|
RootNamespace="sm_mysql"
|
||||||
|
Keyword="Win32Proj"
|
||||||
|
>
|
||||||
|
<Platforms>
|
||||||
|
<Platform
|
||||||
|
Name="Win32"
|
||||||
|
/>
|
||||||
|
</Platforms>
|
||||||
|
<ToolFiles>
|
||||||
|
</ToolFiles>
|
||||||
|
<Configurations>
|
||||||
|
<Configuration
|
||||||
|
Name="Debug|Win32"
|
||||||
|
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||||
|
IntermediateDirectory="$(ConfigurationName)"
|
||||||
|
ConfigurationType="2"
|
||||||
|
CharacterSet="2"
|
||||||
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
Optimization="0"
|
||||||
|
AdditionalIncludeDirectories="..;..\sdk;..\..\..\public;..\..\..\public\sourcepawn"
|
||||||
|
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;SOURCEMOD_BUILD"
|
||||||
|
MinimalRebuild="true"
|
||||||
|
BasicRuntimeChecks="3"
|
||||||
|
RuntimeLibrary="1"
|
||||||
|
StructMemberAlignment="3"
|
||||||
|
UsePrecompiledHeader="0"
|
||||||
|
WarningLevel="3"
|
||||||
|
Detect64BitPortabilityProblems="false"
|
||||||
|
DebugInformationFormat="4"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLinkerTool"
|
||||||
|
AdditionalDependencies="wsock32.lib mysqlclient.lib zlib.lib"
|
||||||
|
OutputFile="$(OutDir)\dbi.mysql.ext.dll"
|
||||||
|
LinkIncremental="2"
|
||||||
|
IgnoreDefaultLibraryNames="LIBCMT"
|
||||||
|
GenerateDebugInformation="true"
|
||||||
|
SubSystem="2"
|
||||||
|
TargetMachine="1"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCALinkTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManifestTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXDCMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCBscMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCFxCopTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCAppVerifierTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebDeploymentTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"
|
||||||
|
/>
|
||||||
|
</Configuration>
|
||||||
|
<Configuration
|
||||||
|
Name="Release|Win32"
|
||||||
|
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||||
|
IntermediateDirectory="$(ConfigurationName)"
|
||||||
|
ConfigurationType="2"
|
||||||
|
CharacterSet="2"
|
||||||
|
WholeProgramOptimization="1"
|
||||||
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
AdditionalIncludeDirectories="..;..\sdk;..\..\..\public;..\..\..\public\sourcepawn"
|
||||||
|
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;SOURCEMOD_BUILD"
|
||||||
|
RuntimeLibrary="0"
|
||||||
|
StructMemberAlignment="3"
|
||||||
|
UsePrecompiledHeader="0"
|
||||||
|
WarningLevel="3"
|
||||||
|
Detect64BitPortabilityProblems="false"
|
||||||
|
DebugInformationFormat="3"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLinkerTool"
|
||||||
|
AdditionalDependencies="wsock32.lib mysqlclient.lib"
|
||||||
|
OutputFile="$(OutDir)\dbi.mysql.ext.dll"
|
||||||
|
LinkIncremental="1"
|
||||||
|
GenerateDebugInformation="true"
|
||||||
|
SubSystem="2"
|
||||||
|
OptimizeReferences="2"
|
||||||
|
EnableCOMDATFolding="2"
|
||||||
|
TargetMachine="1"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCALinkTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManifestTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXDCMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCBscMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCFxCopTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCAppVerifierTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebDeploymentTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"
|
||||||
|
/>
|
||||||
|
</Configuration>
|
||||||
|
</Configurations>
|
||||||
|
<References>
|
||||||
|
</References>
|
||||||
|
<Files>
|
||||||
|
<Filter
|
||||||
|
Name="Source Files"
|
||||||
|
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||||
|
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\extension.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="Header Files"
|
||||||
|
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||||
|
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\extension.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="Resource Files"
|
||||||
|
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
|
||||||
|
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
|
||||||
|
>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="SourceMod SDK"
|
||||||
|
UniqueIdentifier="{31958233-BB2D-4e41-A8F9-CE8A4684F436}"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sdk\smsdk_config.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sdk\smsdk_ext.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sdk\smsdk_ext.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="MySQL Driver"
|
||||||
|
>
|
||||||
|
<Filter
|
||||||
|
Name="Headers"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\mysql\MyBasicResults.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\mysql\MyBoundResults.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\mysql\MyDatabase.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\mysql\MyDriver.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\mysql\MyStatement.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="Source"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\mysql\MyBasicResults.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\mysql\MyBoundResults.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\mysql\MyDatabase.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\mysql\MyDriver.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\mysql\MyStatement.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
</Filter>
|
||||||
|
</Files>
|
||||||
|
<Globals>
|
||||||
|
</Globals>
|
||||||
|
</VisualStudioProject>
|
337
extensions/mysql/mysql/MyBasicResults.cpp
Normal file
337
extensions/mysql/mysql/MyBasicResults.cpp
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include "MyBasicResults.h"
|
||||||
|
|
||||||
|
MyBasicResults::MyBasicResults(MYSQL_RES *res)
|
||||||
|
: m_pRes(res)
|
||||||
|
{
|
||||||
|
Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
MyBasicResults::~MyBasicResults()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyBasicResults::Update()
|
||||||
|
{
|
||||||
|
if (m_pRes)
|
||||||
|
{
|
||||||
|
m_ColCount = (unsigned int)mysql_num_fields(m_pRes);
|
||||||
|
m_RowCount = (unsigned int)mysql_num_rows(m_pRes);
|
||||||
|
m_CurRow = 0;
|
||||||
|
m_Row = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int MyBasicResults::GetRowCount()
|
||||||
|
{
|
||||||
|
return m_RowCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int MyBasicResults::GetFieldCount()
|
||||||
|
{
|
||||||
|
return m_ColCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyBasicResults::FieldNameToNum(const char *name, unsigned int *columnId)
|
||||||
|
{
|
||||||
|
unsigned int total = GetFieldCount();
|
||||||
|
|
||||||
|
for (unsigned int i=0; i<total; i++)
|
||||||
|
{
|
||||||
|
if (strcmp(FieldNumToName(i), name) == 0)
|
||||||
|
{
|
||||||
|
*columnId = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *MyBasicResults::FieldNumToName(unsigned int colId)
|
||||||
|
{
|
||||||
|
if (colId >= GetFieldCount())
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
MYSQL_FIELD *field = mysql_fetch_field_direct(m_pRes, colId);
|
||||||
|
return field ? (field->name ? field->name : "") : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyBasicResults::MoreRows()
|
||||||
|
{
|
||||||
|
return (m_CurRow < m_RowCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
IResultRow *MyBasicResults::FetchRow()
|
||||||
|
{
|
||||||
|
if (m_CurRow >= m_RowCount)
|
||||||
|
{
|
||||||
|
/* Put us one after so we know to block CurrentRow() */
|
||||||
|
m_CurRow = m_RowCount + 1;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
m_Row = mysql_fetch_row(m_pRes);
|
||||||
|
m_Lengths = mysql_fetch_lengths(m_pRes);
|
||||||
|
m_CurRow++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
IResultRow *MyBasicResults::CurrentRow()
|
||||||
|
{
|
||||||
|
if (!m_pRes
|
||||||
|
|| !m_CurRow
|
||||||
|
|| m_CurRow > m_RowCount)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyBasicResults::Rewind()
|
||||||
|
{
|
||||||
|
mysql_data_seek(m_pRes, 0);
|
||||||
|
m_CurRow = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBType MyBasicResults::GetFieldType(unsigned int field)
|
||||||
|
{
|
||||||
|
if (field >= m_ColCount)
|
||||||
|
{
|
||||||
|
return DBType_Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
MYSQL_FIELD *fld = mysql_fetch_field_direct(m_pRes, field);
|
||||||
|
if (!fld)
|
||||||
|
{
|
||||||
|
return DBType_Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetOurType(fld->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
DBType MyBasicResults::GetFieldDataType(unsigned int field)
|
||||||
|
{
|
||||||
|
DBType type = GetFieldType(field);
|
||||||
|
if (type == DBType_Blob)
|
||||||
|
{
|
||||||
|
return DBType_Blob;
|
||||||
|
} else {
|
||||||
|
return DBType_String;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyBasicResults::IsNull(unsigned int columnId)
|
||||||
|
{
|
||||||
|
if (columnId >= m_ColCount)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (m_Row[columnId] == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
DBResult MyBasicResults::GetString(unsigned int columnId, const char **pString, size_t *length)
|
||||||
|
{
|
||||||
|
if (columnId >= m_ColCount)
|
||||||
|
{
|
||||||
|
return DBVal_Error;
|
||||||
|
} else if (m_Row[columnId] == NULL) {
|
||||||
|
*pString = "";
|
||||||
|
if (length)
|
||||||
|
{
|
||||||
|
*length = 0;
|
||||||
|
}
|
||||||
|
return DBVal_Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pString = m_Row[columnId];
|
||||||
|
|
||||||
|
if (length)
|
||||||
|
{
|
||||||
|
*length = (size_t)m_Lengths[columnId];
|
||||||
|
}
|
||||||
|
|
||||||
|
return DBVal_Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBResult MyBasicResults::CopyString(unsigned int columnId,
|
||||||
|
char *buffer,
|
||||||
|
size_t maxlength,
|
||||||
|
size_t *written)
|
||||||
|
{
|
||||||
|
DBResult res;
|
||||||
|
const char *str;
|
||||||
|
if ((res=GetString(columnId, &str, NULL)) == DBVal_Error)
|
||||||
|
{
|
||||||
|
return DBVal_Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t wr = strncopy(buffer, str, maxlength);
|
||||||
|
if (written)
|
||||||
|
{
|
||||||
|
*written = wr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MyBasicResults::GetDataSize(unsigned int columnId)
|
||||||
|
{
|
||||||
|
if (columnId >= m_ColCount)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (size_t)m_Lengths[columnId];
|
||||||
|
}
|
||||||
|
|
||||||
|
DBResult MyBasicResults::GetFloat(unsigned int col, float *fval)
|
||||||
|
{
|
||||||
|
if (col >= m_ColCount)
|
||||||
|
{
|
||||||
|
return DBVal_Error;
|
||||||
|
} else if (m_Row[col] == NULL) {
|
||||||
|
*fval = 0.0f;
|
||||||
|
return DBVal_Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
*fval = (float)atof(m_Row[col]);
|
||||||
|
|
||||||
|
return DBVal_Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBResult MyBasicResults::GetInt(unsigned int col, int *val)
|
||||||
|
{
|
||||||
|
if (col >= m_ColCount)
|
||||||
|
{
|
||||||
|
return DBVal_Error;
|
||||||
|
} else if (m_Row[col] == NULL) {
|
||||||
|
*val = 0;
|
||||||
|
return DBVal_Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
*val = atoi(m_Row[col]);
|
||||||
|
|
||||||
|
return DBVal_Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBResult MyBasicResults::GetBlob(unsigned int col, const void **pData, size_t *length)
|
||||||
|
{
|
||||||
|
if (col >= m_ColCount)
|
||||||
|
{
|
||||||
|
return DBVal_Error;
|
||||||
|
} else if (m_Row[col] == NULL) {
|
||||||
|
*pData = NULL;
|
||||||
|
if (length)
|
||||||
|
{
|
||||||
|
*length = 0;
|
||||||
|
}
|
||||||
|
return DBVal_Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pData = m_Row[col];
|
||||||
|
|
||||||
|
if (length)
|
||||||
|
{
|
||||||
|
*length = (size_t)m_Lengths[col];
|
||||||
|
}
|
||||||
|
|
||||||
|
return DBVal_Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBResult MyBasicResults::CopyBlob(unsigned int columnId, void *buffer, size_t maxlength, size_t *written)
|
||||||
|
{
|
||||||
|
const void *addr;
|
||||||
|
size_t length;
|
||||||
|
DBResult res;
|
||||||
|
|
||||||
|
if ((res=GetBlob(columnId, &addr, &length)) == DBVal_Error)
|
||||||
|
{
|
||||||
|
return DBVal_Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr == NULL)
|
||||||
|
{
|
||||||
|
return DBVal_Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length > maxlength)
|
||||||
|
{
|
||||||
|
length = maxlength;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buffer, addr, length);
|
||||||
|
if (written)
|
||||||
|
{
|
||||||
|
*written = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
MyQuery::MyQuery(MyDatabase *db, MYSQL_RES *res)
|
||||||
|
: m_pParent(db), m_rs(res)
|
||||||
|
{
|
||||||
|
m_pParent->IncRefCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
IResultSet *MyQuery::GetResultSet()
|
||||||
|
{
|
||||||
|
if (m_rs.m_pRes == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &m_rs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyQuery::FetchMoreResults()
|
||||||
|
{
|
||||||
|
if (m_rs.m_pRes == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
} else if (!mysql_more_results(m_pParent->m_mysql)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_free_result(m_rs.m_pRes);
|
||||||
|
m_rs.m_pRes = NULL;
|
||||||
|
|
||||||
|
if (mysql_next_result(m_pParent->m_mysql) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_rs.m_pRes = mysql_store_result(m_pParent->m_mysql);
|
||||||
|
m_rs.Update();
|
||||||
|
|
||||||
|
return (m_rs.m_pRes != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyQuery::Destroy()
|
||||||
|
{
|
||||||
|
/* :TODO: All this rot should be moved into the destructor,
|
||||||
|
* and the Update() function needs to not be so stupid.
|
||||||
|
*/
|
||||||
|
|
||||||
|
while (FetchMoreResults())
|
||||||
|
{
|
||||||
|
/* Spin until all are gone */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free the last, if any */
|
||||||
|
if (m_rs.m_pRes != NULL)
|
||||||
|
{
|
||||||
|
mysql_free_result(m_rs.m_pRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tell our parent we're done */
|
||||||
|
m_pParent->Close();
|
||||||
|
|
||||||
|
/* Self destruct */
|
||||||
|
delete this;
|
||||||
|
}
|
64
extensions/mysql/mysql/MyBasicResults.h
Normal file
64
extensions/mysql/mysql/MyBasicResults.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#ifndef _INCLUDE_SM_MYSQL_BASIC_RESULTS_H_
|
||||||
|
#define _INCLUDE_SM_MYSQL_BASIC_RESULTS_H_
|
||||||
|
|
||||||
|
#include "MyDatabase.h"
|
||||||
|
|
||||||
|
class MyQuery;
|
||||||
|
|
||||||
|
class MyBasicResults :
|
||||||
|
public IResultSet,
|
||||||
|
public IResultRow
|
||||||
|
{
|
||||||
|
friend class MyQuery;
|
||||||
|
public:
|
||||||
|
MyBasicResults(MYSQL_RES *res);
|
||||||
|
~MyBasicResults();
|
||||||
|
public: //IResultSet
|
||||||
|
unsigned int GetRowCount();
|
||||||
|
unsigned int GetFieldCount();
|
||||||
|
const char *FieldNumToName(unsigned int columnId);
|
||||||
|
bool FieldNameToNum(const char *name, unsigned int *columnId);
|
||||||
|
bool MoreRows();
|
||||||
|
IResultRow *FetchRow();
|
||||||
|
bool Rewind();
|
||||||
|
DBType GetFieldType(unsigned int field);
|
||||||
|
DBType GetFieldDataType(unsigned int field);
|
||||||
|
IResultRow *CurrentRow();
|
||||||
|
public: //IResultRow
|
||||||
|
DBResult GetString(unsigned int columnId, const char **pString, size_t *length);
|
||||||
|
DBResult GetFloat(unsigned int columnId, float *pFloat);
|
||||||
|
DBResult GetInt(unsigned int columnId, int *pInt);
|
||||||
|
bool IsNull(unsigned int columnId);
|
||||||
|
DBResult GetBlob(unsigned int columnId, const void **pData, size_t *length);
|
||||||
|
DBResult CopyBlob(unsigned int columnId, void *buffer, size_t maxlength, size_t *written);
|
||||||
|
DBResult CopyString(unsigned int columnId,
|
||||||
|
char *buffer,
|
||||||
|
size_t maxlength,
|
||||||
|
size_t *written);
|
||||||
|
size_t GetDataSize(unsigned int columnId);
|
||||||
|
protected:
|
||||||
|
void Update();
|
||||||
|
private:
|
||||||
|
MYSQL_RES *m_pRes;
|
||||||
|
unsigned int m_CurRow;
|
||||||
|
MYSQL_ROW m_Row;
|
||||||
|
unsigned long *m_Lengths;
|
||||||
|
unsigned int m_ColCount;
|
||||||
|
unsigned int m_RowCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyQuery : public IQuery
|
||||||
|
{
|
||||||
|
friend class MyBasicResults;
|
||||||
|
public:
|
||||||
|
MyQuery(MyDatabase *db, MYSQL_RES *res);
|
||||||
|
public:
|
||||||
|
IResultSet *GetResultSet();
|
||||||
|
bool FetchMoreResults();
|
||||||
|
void Destroy();
|
||||||
|
private:
|
||||||
|
MyDatabase *m_pParent;
|
||||||
|
MyBasicResults m_rs;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //_INCLUDE_SM_MYSQL_BASIC_RESULTS_H_
|
633
extensions/mysql/mysql/MyBoundResults.cpp
Normal file
633
extensions/mysql/mysql/MyBoundResults.cpp
Normal file
@ -0,0 +1,633 @@
|
|||||||
|
#include "MyBoundResults.h"
|
||||||
|
|
||||||
|
#define DEFAULT_BUFFER_SIZE 5
|
||||||
|
|
||||||
|
/* :IDEA: When we have to refetch a buffer to do type changes, should we rebind
|
||||||
|
* the buffer so the next fetch will predict the proper cast? Probably yes since
|
||||||
|
* these things are done in standard iterations, but maybe users should be punished
|
||||||
|
* for not using the API as it was intended? Maybe it should be an option set to
|
||||||
|
* on by default to catch the bad users?
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum_field_types GetTheirType(DBType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case DBType_Float:
|
||||||
|
{
|
||||||
|
return MYSQL_TYPE_FLOAT;
|
||||||
|
}
|
||||||
|
case DBType_Integer:
|
||||||
|
{
|
||||||
|
return MYSQL_TYPE_LONG;
|
||||||
|
}
|
||||||
|
case DBType_String:
|
||||||
|
{
|
||||||
|
return MYSQL_TYPE_STRING;
|
||||||
|
}
|
||||||
|
case DBType_Blob:
|
||||||
|
{
|
||||||
|
return MYSQL_TYPE_BLOB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MYSQL_TYPE_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
MyBoundResults::MyBoundResults(MYSQL_STMT *stmt, MYSQL_RES *res)
|
||||||
|
: m_stmt(stmt), m_pRes(res), m_Initialized(false), m_RowCount(0), m_CurRow(0)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Important things to note here:
|
||||||
|
* 1) We're guaranteed at least one field.
|
||||||
|
* 2) The field information should never change, and thus we
|
||||||
|
* never rebuild it. If someone ALTERs the table during
|
||||||
|
* a prepared query's lifetime, it's their own death.
|
||||||
|
*/
|
||||||
|
|
||||||
|
m_ColCount = (unsigned int)mysql_num_fields(m_pRes);
|
||||||
|
|
||||||
|
/* Allocate buffers */
|
||||||
|
m_bind = (MYSQL_BIND *)malloc(sizeof(MYSQL_BIND) * m_ColCount);
|
||||||
|
m_pull = (ResultBind *)malloc(sizeof(ResultBind) * m_ColCount);
|
||||||
|
|
||||||
|
/* Zero data */
|
||||||
|
memset(m_bind, 0, sizeof(MYSQL_BIND) * m_ColCount);
|
||||||
|
memset(m_pull, 0, sizeof(ResultBind) * m_ColCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
MyBoundResults::~MyBoundResults()
|
||||||
|
{
|
||||||
|
if (m_Initialized)
|
||||||
|
{
|
||||||
|
/* Make sure we free our internal buffers */
|
||||||
|
for (unsigned int i=0; i<m_ColCount; i++)
|
||||||
|
{
|
||||||
|
delete [] m_pull[i].blob;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(m_pull);
|
||||||
|
free(m_bind);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyBoundResults::Update()
|
||||||
|
{
|
||||||
|
m_RowCount = (unsigned int)mysql_stmt_num_rows(m_stmt);
|
||||||
|
m_CurRow = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyBoundResults::Initialize()
|
||||||
|
{
|
||||||
|
/* Check if we need to build our result binding information */
|
||||||
|
if (!m_Initialized)
|
||||||
|
{
|
||||||
|
for (unsigned int i=0; i<m_ColCount; i++)
|
||||||
|
{
|
||||||
|
MYSQL_FIELD *field = mysql_fetch_field_direct(m_pRes, i);
|
||||||
|
DBType type = GetOurType(field->type);
|
||||||
|
|
||||||
|
m_bind[i].length = &(m_pull[i].my_length);
|
||||||
|
m_bind[i].is_null = &(m_pull[i].my_null);
|
||||||
|
|
||||||
|
if (type == DBType_Integer)
|
||||||
|
{
|
||||||
|
m_bind[i].buffer_type = MYSQL_TYPE_LONG;
|
||||||
|
m_bind[i].buffer = &(m_pull[i].data.ival);
|
||||||
|
} else if (type == DBType_Float) {
|
||||||
|
m_bind[i].buffer_type = MYSQL_TYPE_FLOAT;
|
||||||
|
m_bind[i].buffer = &(m_pull[i].data.ival);
|
||||||
|
} else if (type == DBType_String || type == DBType_Blob) {
|
||||||
|
m_bind[i].buffer_type = GetTheirType(type);
|
||||||
|
|
||||||
|
/* We bound this to 2048 bytes. Otherwise a MEDIUMBLOB
|
||||||
|
* or something could allocate horrible amounts of memory
|
||||||
|
* because MySQL is incompetent.
|
||||||
|
*/
|
||||||
|
size_t creat_length = (size_t)field->length;
|
||||||
|
if (!creat_length || creat_length > DEFAULT_BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
creat_length = DEFAULT_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
m_pull[i].blob = new unsigned char[creat_length];
|
||||||
|
m_pull[i].length = creat_length;
|
||||||
|
|
||||||
|
m_bind[i].buffer = m_pull[i].blob;
|
||||||
|
m_bind[i].buffer_length = (unsigned long)creat_length;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_Initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do the actual bind */
|
||||||
|
return (mysql_stmt_bind_result(m_stmt, m_bind) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int MyBoundResults::GetRowCount()
|
||||||
|
{
|
||||||
|
return m_RowCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int MyBoundResults::GetFieldCount()
|
||||||
|
{
|
||||||
|
return m_ColCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *MyBoundResults::FieldNumToName(unsigned int columnId)
|
||||||
|
{
|
||||||
|
if (columnId >= m_ColCount)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
MYSQL_FIELD *field = mysql_fetch_field_direct(m_pRes, columnId);
|
||||||
|
|
||||||
|
return field ? (field->name ? field->name : "") : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyBoundResults::FieldNameToNum(const char *name, unsigned int *columnId)
|
||||||
|
{
|
||||||
|
for (unsigned int i=0; i<m_ColCount; i++)
|
||||||
|
{
|
||||||
|
if (strcmp(name, FieldNumToName(i)) == 0)
|
||||||
|
{
|
||||||
|
*columnId = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyBoundResults::MoreRows()
|
||||||
|
{
|
||||||
|
return (m_CurRow < m_RowCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
IResultRow *MyBoundResults::FetchRow()
|
||||||
|
{
|
||||||
|
if (!MoreRows())
|
||||||
|
{
|
||||||
|
m_CurRow = m_RowCount + 1;
|
||||||
|
NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_CurRow++;
|
||||||
|
|
||||||
|
/* We should be able to get another row */
|
||||||
|
int err = mysql_stmt_fetch(m_stmt);
|
||||||
|
if (err == 0 || err == MYSQL_DATA_TRUNCATED)
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == MYSQL_NO_DATA && m_CurRow == m_RowCount)
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Some sort of error occurred */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
IResultRow *MyBoundResults::CurrentRow()
|
||||||
|
{
|
||||||
|
if (!m_CurRow || m_CurRow > m_RowCount)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyBoundResults::Rewind()
|
||||||
|
{
|
||||||
|
mysql_stmt_data_seek(m_stmt, 0);
|
||||||
|
m_CurRow = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBType MyBoundResults::GetFieldType(unsigned int field)
|
||||||
|
{
|
||||||
|
if (field >= m_ColCount)
|
||||||
|
{
|
||||||
|
return DBType_Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
MYSQL_FIELD *fld = mysql_fetch_field_direct(m_pRes, field);
|
||||||
|
return GetOurType(fld->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
DBType MyBoundResults::GetFieldDataType(unsigned int field)
|
||||||
|
{
|
||||||
|
return GetFieldType(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResizeBuffer(ResultBind *bind, size_t len)
|
||||||
|
{
|
||||||
|
if (!bind->blob)
|
||||||
|
{
|
||||||
|
bind->blob = new unsigned char[len];
|
||||||
|
bind->length = len;
|
||||||
|
} else if (bind->length < len) {
|
||||||
|
delete [] bind->blob;
|
||||||
|
bind->blob = new unsigned char[len];
|
||||||
|
bind->length = len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RefetchField(MYSQL_STMT *stmt,
|
||||||
|
ResultBind *rbind,
|
||||||
|
unsigned int id,
|
||||||
|
size_t initSize,
|
||||||
|
enum_field_types type)
|
||||||
|
{
|
||||||
|
/* Make sure there is a buffer to pull into */
|
||||||
|
ResizeBuffer(rbind, initSize);
|
||||||
|
|
||||||
|
MYSQL_BIND bind;
|
||||||
|
|
||||||
|
/* Initialize bind info */
|
||||||
|
memset(&bind, 0, sizeof(MYSQL_BIND));
|
||||||
|
bind.buffer = rbind->blob;
|
||||||
|
bind.buffer_type = type;
|
||||||
|
bind.buffer_length = (unsigned long)rbind->length;
|
||||||
|
bind.length = &(rbind->my_length);
|
||||||
|
bind.is_null = &(rbind->my_null);
|
||||||
|
|
||||||
|
/* Attempt to fetch */
|
||||||
|
return (mysql_stmt_fetch_column(stmt, &bind, id, 0) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
DBResult RefetchSize4Field(MYSQL_STMT *stmt,
|
||||||
|
unsigned int id,
|
||||||
|
void *buffer,
|
||||||
|
enum_field_types type)
|
||||||
|
{
|
||||||
|
MYSQL_BIND bind;
|
||||||
|
my_bool is_null;
|
||||||
|
|
||||||
|
/* Initialize bind info */
|
||||||
|
memset(&bind, 0, sizeof(MYSQL_BIND));
|
||||||
|
bind.buffer = buffer;
|
||||||
|
bind.buffer_type = type;
|
||||||
|
bind.is_null = &is_null;
|
||||||
|
|
||||||
|
/* Attempt to fetch */
|
||||||
|
if (mysql_stmt_fetch_column(stmt, &bind, id, 0) != 0)
|
||||||
|
{
|
||||||
|
return DBVal_TypeMismatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_null ? DBVal_Null : DBVal_Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RefetchUserField(MYSQL_STMT *stmt,
|
||||||
|
unsigned int id,
|
||||||
|
void *userbuf,
|
||||||
|
size_t userlen,
|
||||||
|
enum_field_types type,
|
||||||
|
my_bool &is_null,
|
||||||
|
size_t *written)
|
||||||
|
{
|
||||||
|
MYSQL_BIND bind;
|
||||||
|
unsigned long length;
|
||||||
|
|
||||||
|
/* Initialize bind info */
|
||||||
|
memset(&bind, 0, sizeof(MYSQL_BIND));
|
||||||
|
bind.buffer = userbuf;
|
||||||
|
bind.buffer_type = type;
|
||||||
|
bind.length = &length;
|
||||||
|
bind.is_null = &is_null;
|
||||||
|
bind.buffer_length = (unsigned long)userlen;
|
||||||
|
|
||||||
|
if (mysql_stmt_fetch_column(stmt, &bind, id, 0) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == MYSQL_TYPE_STRING && (size_t)length == userlen)
|
||||||
|
{
|
||||||
|
/* Enforce null termination in case MySQL forgot.
|
||||||
|
* Note we subtract one from the length (which must be >= 1)
|
||||||
|
* so we can pass the number of bytes written below.
|
||||||
|
*/
|
||||||
|
char *data = (char *)userbuf;
|
||||||
|
data[--userlen] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (written)
|
||||||
|
{
|
||||||
|
/* In the case of strings, they will never be equal */
|
||||||
|
*written = (userlen < length) ? userlen : length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BAD_COL_CHECK() \
|
||||||
|
if (id >= m_ColCount) \
|
||||||
|
return DBVal_Error;
|
||||||
|
|
||||||
|
#define STR_NULL_CHECK_0(var) \
|
||||||
|
if (var) { \
|
||||||
|
*pString = NULL; \
|
||||||
|
if (length) \
|
||||||
|
*length = 0; \
|
||||||
|
return DBVal_Null; \
|
||||||
|
}
|
||||||
|
|
||||||
|
DBResult MyBoundResults::GetString(unsigned int id, const char **pString, size_t *length)
|
||||||
|
{
|
||||||
|
BAD_COL_CHECK();
|
||||||
|
|
||||||
|
STR_NULL_CHECK_0(m_pull[id].my_null);
|
||||||
|
|
||||||
|
if (m_bind[id].buffer_type != MYSQL_TYPE_STRING)
|
||||||
|
{
|
||||||
|
/* Ugh, we have to re-get this as a string. Sigh, stupid user.
|
||||||
|
* We're going to disallow conversions from blobs.
|
||||||
|
*/
|
||||||
|
if (m_bind[id].buffer_type == MYSQL_TYPE_BLOB)
|
||||||
|
{
|
||||||
|
return DBVal_TypeMismatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attempt to refetch the string */
|
||||||
|
if (!RefetchField(m_stmt, &m_pull[id], id, 128, MYSQL_TYPE_STRING))
|
||||||
|
{
|
||||||
|
return DBVal_TypeMismatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we have a new null */
|
||||||
|
STR_NULL_CHECK_0(m_pull[id].my_null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Okay, we should now have a blob type whether we originally wanted one or not. */
|
||||||
|
|
||||||
|
/* Check if the size is too small. Note that MySQL will not null terminate small buffers,
|
||||||
|
* and it returns the size without the null terminator. This means we need to add an extra
|
||||||
|
* byte onto the end to accept the terminator until there is a workaround.
|
||||||
|
*
|
||||||
|
* Note that we do an >= check because MySQL appears to want the null terminator included,
|
||||||
|
* so just to be safe and avoid its inconsistencies, we make sure we'll always have room.
|
||||||
|
*/
|
||||||
|
if ((size_t)(m_pull[id].my_length) >= m_pull[id].length)
|
||||||
|
{
|
||||||
|
/* Yes, we need to refetch. */
|
||||||
|
if (!RefetchField(m_stmt, &m_pull[id], id, m_pull[id].my_length + 1, MYSQL_TYPE_STRING))
|
||||||
|
{
|
||||||
|
return DBVal_Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finally, we can return. We're guaranteed to have a properly NULL-terminated string
|
||||||
|
* here because we have refetched the string to a bigger length.
|
||||||
|
*/
|
||||||
|
*pString = (const char *)m_pull[id].blob;
|
||||||
|
if (length)
|
||||||
|
{
|
||||||
|
*length = (size_t)m_pull[id].my_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DBVal_Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STR_NULL_CHECK_1(var) \
|
||||||
|
if (var) { \
|
||||||
|
buffer[0] = '\0'; \
|
||||||
|
if (written) \
|
||||||
|
*written = 0; \
|
||||||
|
return DBVal_Null; \
|
||||||
|
}
|
||||||
|
|
||||||
|
DBResult MyBoundResults::CopyString(unsigned int id, char *buffer, size_t maxlength, size_t *written)
|
||||||
|
{
|
||||||
|
BAD_COL_CHECK();
|
||||||
|
|
||||||
|
STR_NULL_CHECK_1(m_pull[id].my_null);
|
||||||
|
|
||||||
|
if (!buffer || !maxlength)
|
||||||
|
{
|
||||||
|
return DBVal_Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_bind[id].buffer_type != MYSQL_TYPE_STRING)
|
||||||
|
{
|
||||||
|
/* We're going to disallow conversions from blobs. */
|
||||||
|
if (m_bind[id].buffer_type == MYSQL_TYPE_BLOB)
|
||||||
|
{
|
||||||
|
return DBVal_TypeMismatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Re-fetch this for the user. This call will guarantee NULL termination. */
|
||||||
|
my_bool is_null;
|
||||||
|
if (!RefetchUserField(m_stmt, id, buffer, maxlength, MYSQL_TYPE_STRING, is_null, written))
|
||||||
|
{
|
||||||
|
return DBVal_TypeMismatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
STR_NULL_CHECK_1(is_null);
|
||||||
|
|
||||||
|
return DBVal_Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pull_length = (size_t)m_pull[id].my_length;
|
||||||
|
size_t orig_length = m_pull[id].length;
|
||||||
|
|
||||||
|
/* If there's more data in the buffer, we have to look at two cases. */
|
||||||
|
if (pull_length >= orig_length)
|
||||||
|
{
|
||||||
|
/* If the user supplied a bigger buffer, just refetch for them. */
|
||||||
|
if (maxlength > orig_length)
|
||||||
|
{
|
||||||
|
my_bool is_null;
|
||||||
|
RefetchUserField(m_stmt, id, buffer, maxlength, MYSQL_TYPE_STRING, is_null, written);
|
||||||
|
STR_NULL_CHECK_1(is_null);
|
||||||
|
return DBVal_Data;
|
||||||
|
}
|
||||||
|
/* Otherwise, we should enforce null termination from MySQL. */
|
||||||
|
else if (pull_length == orig_length)
|
||||||
|
{
|
||||||
|
char *data = (char *)m_pull[id].blob;
|
||||||
|
data[pull_length] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we got here, we need to copy the resultant string to the user and be done with it.
|
||||||
|
* Null termination is guaranteed from the pulled string.
|
||||||
|
*/
|
||||||
|
size_t wr = strncopy(buffer, (const char *)m_pull[id].blob, maxlength);
|
||||||
|
if (written)
|
||||||
|
{
|
||||||
|
*written = wr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DBVal_Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBResult MyBoundResults::GetFloat(unsigned int id, float *pFloat)
|
||||||
|
{
|
||||||
|
BAD_COL_CHECK();
|
||||||
|
|
||||||
|
if (m_pull[id].my_null)
|
||||||
|
{
|
||||||
|
*pFloat = 0.0f;
|
||||||
|
return DBVal_Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_bind[id].buffer_type != MYSQL_TYPE_FLOAT)
|
||||||
|
{
|
||||||
|
if (m_bind[id].buffer_type == MYSQL_TYPE_BLOB)
|
||||||
|
{
|
||||||
|
return DBVal_TypeMismatch;
|
||||||
|
}
|
||||||
|
/* We have to convert... */
|
||||||
|
return RefetchSize4Field(m_stmt, id, pFloat, MYSQL_TYPE_FLOAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
*pFloat = m_pull[id].data.fval;
|
||||||
|
|
||||||
|
return DBVal_Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBResult MyBoundResults::GetInt(unsigned int id, int *pInt)
|
||||||
|
{
|
||||||
|
BAD_COL_CHECK();
|
||||||
|
|
||||||
|
if (m_pull[id].my_null)
|
||||||
|
{
|
||||||
|
*pInt = 0;
|
||||||
|
return DBVal_Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_bind[id].buffer_type != MYSQL_TYPE_LONG)
|
||||||
|
{
|
||||||
|
if (m_bind[id].buffer_type == MYSQL_TYPE_BLOB)
|
||||||
|
{
|
||||||
|
return DBVal_TypeMismatch;
|
||||||
|
}
|
||||||
|
/* We have to convert... */
|
||||||
|
return RefetchSize4Field(m_stmt, id, pInt, MYSQL_TYPE_LONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
*pInt = m_pull[id].data.ival;
|
||||||
|
|
||||||
|
return DBVal_Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyBoundResults::IsNull(unsigned int id)
|
||||||
|
{
|
||||||
|
if (id >= m_ColCount)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_pull[id].my_null ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BLOB_CHECK_NULL_0() \
|
||||||
|
if (m_pull[id].my_null) { \
|
||||||
|
*pData = NULL; \
|
||||||
|
if (length) \
|
||||||
|
*length = 0; \
|
||||||
|
return DBVal_Null; \
|
||||||
|
}
|
||||||
|
|
||||||
|
DBResult MyBoundResults::GetBlob(unsigned int id, const void **pData, size_t *length)
|
||||||
|
{
|
||||||
|
BAD_COL_CHECK();
|
||||||
|
|
||||||
|
BLOB_CHECK_NULL_0();
|
||||||
|
|
||||||
|
/* We only want blobs to be read as blobs */
|
||||||
|
if (m_bind[id].buffer_type != MYSQL_TYPE_BLOB)
|
||||||
|
{
|
||||||
|
return DBVal_TypeMismatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((size_t)m_pull[id].my_length > m_pull[id].length)
|
||||||
|
{
|
||||||
|
if (!RefetchField(m_stmt, &m_pull[id], id, m_pull[id].my_length, MYSQL_TYPE_BLOB))
|
||||||
|
{
|
||||||
|
return DBVal_TypeMismatch;
|
||||||
|
}
|
||||||
|
BLOB_CHECK_NULL_0();
|
||||||
|
}
|
||||||
|
|
||||||
|
*pData = m_pull[id].blob;
|
||||||
|
if (length)
|
||||||
|
{
|
||||||
|
*length = (size_t)m_pull[id].my_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DBVal_Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BLOB_CHECK_NULL_1(var) \
|
||||||
|
if (var) { \
|
||||||
|
if (written) \
|
||||||
|
*written = 0; \
|
||||||
|
return DBVal_Null; \
|
||||||
|
}
|
||||||
|
|
||||||
|
DBResult MyBoundResults::CopyBlob(unsigned int id, void *buffer, size_t maxlength, size_t *written)
|
||||||
|
{
|
||||||
|
BAD_COL_CHECK();
|
||||||
|
|
||||||
|
/* We only want blobs to be read as blobs */
|
||||||
|
if (m_bind[id].buffer_type != MYSQL_TYPE_BLOB)
|
||||||
|
{
|
||||||
|
return DBVal_TypeMismatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
BLOB_CHECK_NULL_1(m_pull[id].my_null);
|
||||||
|
|
||||||
|
size_t pull_size = (size_t)m_pull[id].my_length;
|
||||||
|
size_t push_size = m_pull[id].length;
|
||||||
|
|
||||||
|
/* Check if we can do a resize and copy in one step */
|
||||||
|
if (pull_size > push_size
|
||||||
|
&& maxlength > push_size)
|
||||||
|
{
|
||||||
|
my_bool is_null;
|
||||||
|
if (!RefetchUserField(m_stmt, id, buffer, maxlength, MYSQL_TYPE_BLOB, is_null, written))
|
||||||
|
{
|
||||||
|
return DBVal_TypeMismatch;
|
||||||
|
}
|
||||||
|
BLOB_CHECK_NULL_1(is_null);
|
||||||
|
return DBVal_Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we got here, either there is no more data to refetch,
|
||||||
|
* or our buffer is too small to receive the refetched data.
|
||||||
|
*/
|
||||||
|
size_t buf_bytes = pull_size > push_size ? push_size : pull_size;
|
||||||
|
size_t to_copy = buf_bytes > maxlength ? maxlength : buf_bytes;
|
||||||
|
|
||||||
|
memcpy(buffer, m_pull[id].blob, to_copy);
|
||||||
|
if (written)
|
||||||
|
{
|
||||||
|
*written = to_copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DBVal_Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MyBoundResults::GetDataSize(unsigned int id)
|
||||||
|
{
|
||||||
|
if (id >= m_ColCount)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (size_t)m_pull[id].my_length;
|
||||||
|
}
|
66
extensions/mysql/mysql/MyBoundResults.h
Normal file
66
extensions/mysql/mysql/MyBoundResults.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#ifndef _INCLUDE_SM_MYSQL_BOUND_RESULTS_H_
|
||||||
|
#define _INCLUDE_SM_MYSQL_BOUND_RESULTS_H_
|
||||||
|
|
||||||
|
#include "MyDatabase.h"
|
||||||
|
|
||||||
|
class MyStatement;
|
||||||
|
|
||||||
|
struct ResultBind
|
||||||
|
{
|
||||||
|
my_bool my_null;
|
||||||
|
unsigned long my_length;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
int ival;
|
||||||
|
float fval;
|
||||||
|
} data;
|
||||||
|
unsigned char *blob;
|
||||||
|
size_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyBoundResults :
|
||||||
|
public IResultSet,
|
||||||
|
public IResultRow
|
||||||
|
{
|
||||||
|
friend class MyStatement;
|
||||||
|
public:
|
||||||
|
MyBoundResults(MYSQL_STMT *stmt, MYSQL_RES *res);
|
||||||
|
~MyBoundResults();
|
||||||
|
public: //IResultSet
|
||||||
|
unsigned int GetRowCount();
|
||||||
|
unsigned int GetFieldCount();
|
||||||
|
const char *FieldNumToName(unsigned int columnId);
|
||||||
|
bool FieldNameToNum(const char *name, unsigned int *columnId);
|
||||||
|
bool MoreRows();
|
||||||
|
IResultRow *FetchRow();
|
||||||
|
bool Rewind();
|
||||||
|
DBType GetFieldType(unsigned int field);
|
||||||
|
DBType GetFieldDataType(unsigned int field);
|
||||||
|
IResultRow *CurrentRow();
|
||||||
|
public: //IResultRow
|
||||||
|
DBResult GetString(unsigned int id, const char **pString, size_t *length);
|
||||||
|
DBResult CopyString(unsigned int id,
|
||||||
|
char *buffer,
|
||||||
|
size_t maxlength,
|
||||||
|
size_t *written);
|
||||||
|
DBResult GetFloat(unsigned int id, float *pFloat);
|
||||||
|
DBResult GetInt(unsigned int id, int *pInt);
|
||||||
|
bool IsNull(unsigned int id);
|
||||||
|
size_t GetDataSize(unsigned int id);
|
||||||
|
DBResult GetBlob(unsigned int id, const void **pData, size_t *length);
|
||||||
|
DBResult CopyBlob(unsigned int id, void *buffer, size_t maxlength, size_t *written);
|
||||||
|
public:
|
||||||
|
bool Initialize();
|
||||||
|
void Update();
|
||||||
|
private:
|
||||||
|
MYSQL_STMT *m_stmt;
|
||||||
|
MYSQL_RES *m_pRes;
|
||||||
|
MYSQL_BIND *m_bind;
|
||||||
|
ResultBind *m_pull;
|
||||||
|
unsigned int m_ColCount;
|
||||||
|
bool m_Initialized;
|
||||||
|
unsigned int m_RowCount;
|
||||||
|
unsigned int m_CurRow;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //_INCLUDE_SM_MYSQL_BOUND_RESULTS_H_
|
244
extensions/mysql/mysql/MyDatabase.cpp
Normal file
244
extensions/mysql/mysql/MyDatabase.cpp
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
#include "MyDatabase.h"
|
||||||
|
#include "smsdk_ext.h"
|
||||||
|
#include "MyBasicResults.h"
|
||||||
|
#include "MyStatement.h"
|
||||||
|
|
||||||
|
DBType GetOurType(enum_field_types type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case MYSQL_TYPE_DOUBLE:
|
||||||
|
case MYSQL_TYPE_FLOAT:
|
||||||
|
{
|
||||||
|
return DBType_Float;
|
||||||
|
}
|
||||||
|
case MYSQL_TYPE_TINY:
|
||||||
|
case MYSQL_TYPE_SHORT:
|
||||||
|
case MYSQL_TYPE_LONG:
|
||||||
|
case MYSQL_TYPE_INT24:
|
||||||
|
case MYSQL_TYPE_YEAR:
|
||||||
|
case MYSQL_TYPE_BIT:
|
||||||
|
{
|
||||||
|
return DBType_Integer;
|
||||||
|
}
|
||||||
|
case MYSQL_TYPE_LONGLONG:
|
||||||
|
case MYSQL_TYPE_DATE:
|
||||||
|
case MYSQL_TYPE_TIME:
|
||||||
|
case MYSQL_TYPE_DATETIME:
|
||||||
|
case MYSQL_TYPE_TIMESTAMP:
|
||||||
|
case MYSQL_TYPE_NEWDATE:
|
||||||
|
case MYSQL_TYPE_VAR_STRING:
|
||||||
|
case MYSQL_TYPE_VARCHAR:
|
||||||
|
case MYSQL_TYPE_STRING:
|
||||||
|
case MYSQL_TYPE_NEWDECIMAL:
|
||||||
|
case MYSQL_TYPE_DECIMAL:
|
||||||
|
case MYSQL_TYPE_ENUM:
|
||||||
|
case MYSQL_TYPE_SET:
|
||||||
|
{
|
||||||
|
return DBType_String;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MYSQL_TYPE_TINY_BLOB:
|
||||||
|
case MYSQL_TYPE_MEDIUM_BLOB:
|
||||||
|
case MYSQL_TYPE_LONG_BLOB:
|
||||||
|
case MYSQL_TYPE_BLOB:
|
||||||
|
case MYSQL_TYPE_GEOMETRY:
|
||||||
|
{
|
||||||
|
return DBType_Blob;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
return DBType_String;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DBType_Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
MyDatabase::MyDatabase(MYSQL *mysql, const DatabaseInfo *info, bool persistent)
|
||||||
|
: m_mysql(mysql), m_refcount(1), m_handle(BAD_HANDLE), m_bPersistent(persistent)
|
||||||
|
{
|
||||||
|
m_Host.assign(info->host);
|
||||||
|
m_Database.assign(info->database);
|
||||||
|
m_User.assign(info->user);
|
||||||
|
m_Pass.assign(info->pass);
|
||||||
|
|
||||||
|
m_Info.database = m_Database.c_str();
|
||||||
|
m_Info.host = m_Host.c_str();
|
||||||
|
m_Info.user = m_User.c_str();
|
||||||
|
m_Info.pass = m_Pass.c_str();
|
||||||
|
m_Info.driver = NULL;
|
||||||
|
m_Info.maxTimeout = info->maxTimeout;
|
||||||
|
m_Info.port = info->port;
|
||||||
|
}
|
||||||
|
|
||||||
|
MyDatabase::~MyDatabase()
|
||||||
|
{
|
||||||
|
mysql_close(m_mysql);
|
||||||
|
m_mysql = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyDatabase::IncRefCount()
|
||||||
|
{
|
||||||
|
m_refcount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyDatabase::Close(bool fromHndlSys)
|
||||||
|
{
|
||||||
|
if (m_refcount > 1)
|
||||||
|
{
|
||||||
|
m_refcount--;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we don't have a Handle and the Handle is
|
||||||
|
* is from the Handle System, it means we need
|
||||||
|
* to block a re-entrant call from our own
|
||||||
|
* FreeHandle().
|
||||||
|
*/
|
||||||
|
if (fromHndlSys && (m_handle == BAD_HANDLE))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove us from the search list */
|
||||||
|
if (m_bPersistent)
|
||||||
|
{
|
||||||
|
g_MyDriver.RemoveFromList(this, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we're not from the Handle system, and
|
||||||
|
* we have a Handle, we need to free it first.
|
||||||
|
*/
|
||||||
|
if (!fromHndlSys && m_handle != BAD_HANDLE)
|
||||||
|
{
|
||||||
|
Handle_t hndl = m_handle;
|
||||||
|
m_handle = BAD_HANDLE;
|
||||||
|
dbi->ReleaseHandle(hndl, DBHandle_Database, myself->GetIdentity());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finally, free our resource(s) */
|
||||||
|
delete this;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle_t MyDatabase::GetHandle()
|
||||||
|
{
|
||||||
|
if (m_handle == BAD_HANDLE)
|
||||||
|
{
|
||||||
|
m_handle = dbi->CreateHandle(DBHandle_Database, this, myself->GetIdentity());
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DatabaseInfo &MyDatabase::GetInfo()
|
||||||
|
{
|
||||||
|
return m_Info;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int MyDatabase::GetInsertID()
|
||||||
|
{
|
||||||
|
return (unsigned int)mysql_insert_id(m_mysql);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int MyDatabase::GetAffectedRows()
|
||||||
|
{
|
||||||
|
return (unsigned int)mysql_affected_rows(m_mysql);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *MyDatabase::GetError(int *errCode)
|
||||||
|
{
|
||||||
|
if (errCode)
|
||||||
|
{
|
||||||
|
*errCode = mysql_errno(m_mysql);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mysql_error(m_mysql);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyDatabase::QuoteString(const char *str, char buffer[], size_t maxlength, size_t *newSize)
|
||||||
|
{
|
||||||
|
unsigned long size = static_cast<unsigned long>(strlen(str));
|
||||||
|
unsigned long needed = size * 2 + 1;
|
||||||
|
|
||||||
|
if (maxlength < needed)
|
||||||
|
{
|
||||||
|
if (newSize)
|
||||||
|
{
|
||||||
|
*newSize = (size_t)needed;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
needed = mysql_real_escape_string(m_mysql, buffer, str, size);
|
||||||
|
if (newSize)
|
||||||
|
{
|
||||||
|
*newSize = (size_t)needed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyDatabase::DoSimpleQuery(const char *query)
|
||||||
|
{
|
||||||
|
IQuery *pQuery = DoQuery(query);
|
||||||
|
if (!pQuery)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pQuery->Destroy();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IQuery *MyDatabase::DoQuery(const char *query)
|
||||||
|
{
|
||||||
|
if (mysql_real_query(m_mysql, query, strlen(query)) != 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
MYSQL_RES *res = NULL;
|
||||||
|
if (mysql_field_count(m_mysql))
|
||||||
|
{
|
||||||
|
res = mysql_store_result(m_mysql);
|
||||||
|
if (!res)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MyQuery(this, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPreparedQuery *MyDatabase::PrepareQuery(const char *query, char *error, size_t maxlength, int *errCode)
|
||||||
|
{
|
||||||
|
MYSQL_STMT *stmt = mysql_stmt_init(m_mysql);
|
||||||
|
if (!stmt)
|
||||||
|
{
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
strncopy(error, GetError(errCode), maxlength);
|
||||||
|
} else if (errCode) {
|
||||||
|
*errCode = mysql_errno(m_mysql);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mysql_stmt_prepare(stmt, query, strlen(query)) != 0)
|
||||||
|
{
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
strncopy(error, mysql_stmt_error(stmt), maxlength);
|
||||||
|
}
|
||||||
|
if (errCode)
|
||||||
|
{
|
||||||
|
*errCode = mysql_stmt_errno(stmt);
|
||||||
|
}
|
||||||
|
mysql_stmt_close(stmt);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MyStatement(this, stmt);
|
||||||
|
}
|
45
extensions/mysql/mysql/MyDatabase.h
Normal file
45
extensions/mysql/mysql/MyDatabase.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#ifndef _INCLUDE_SM_MYSQL_DATABASE_H_
|
||||||
|
#define _INCLUDE_SM_MYSQL_DATABASE_H_
|
||||||
|
|
||||||
|
#include "MyDriver.h"
|
||||||
|
|
||||||
|
class MyQuery;
|
||||||
|
class MyStatement;
|
||||||
|
|
||||||
|
class MyDatabase : public IDatabase
|
||||||
|
{
|
||||||
|
friend class MyQuery;
|
||||||
|
friend class MyStatement;
|
||||||
|
public:
|
||||||
|
MyDatabase(MYSQL *mysql, const DatabaseInfo *info, bool persistent);
|
||||||
|
~MyDatabase();
|
||||||
|
public: //IDatabase
|
||||||
|
bool Close(bool fromHndlSys=false);
|
||||||
|
const char *GetError(int *errorCode=NULL);
|
||||||
|
bool DoSimpleQuery(const char *query);
|
||||||
|
IQuery *DoQuery(const char *query);
|
||||||
|
IPreparedQuery *PrepareQuery(const char *query, char *error, size_t maxlength, int *errCode=NULL);
|
||||||
|
bool QuoteString(const char *str, char buffer[], size_t maxlen, size_t *newSize);
|
||||||
|
unsigned int GetAffectedRows();
|
||||||
|
unsigned int GetInsertID();
|
||||||
|
Handle_t GetHandle();
|
||||||
|
public:
|
||||||
|
const DatabaseInfo &GetInfo();
|
||||||
|
void IncRefCount();
|
||||||
|
private:
|
||||||
|
MYSQL *m_mysql;
|
||||||
|
unsigned int m_refcount;
|
||||||
|
Handle_t m_handle;
|
||||||
|
|
||||||
|
/* ---------- */
|
||||||
|
DatabaseInfo m_Info;
|
||||||
|
String m_Host;
|
||||||
|
String m_Database;
|
||||||
|
String m_User;
|
||||||
|
String m_Pass;
|
||||||
|
bool m_bPersistent;
|
||||||
|
};
|
||||||
|
|
||||||
|
DBType GetOurType(enum_field_types type);
|
||||||
|
|
||||||
|
#endif //_INCLUDE_SM_MYSQL_DATABASE_H_
|
169
extensions/mysql/mysql/MyDriver.cpp
Normal file
169
extensions/mysql/mysql/MyDriver.cpp
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
#include "MyDriver.h"
|
||||||
|
#include "MyDatabase.h"
|
||||||
|
#include "sdk/smsdk_ext.h"
|
||||||
|
|
||||||
|
MyDriver g_MyDriver;
|
||||||
|
|
||||||
|
MyDriver::MyDriver()
|
||||||
|
{
|
||||||
|
m_MyHandle = BAD_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseDBList(List<MyDatabase *> &l)
|
||||||
|
{
|
||||||
|
List<MyDatabase *>::iterator iter;
|
||||||
|
for (iter=l.begin(); iter!=l.end(); iter++)
|
||||||
|
{
|
||||||
|
MyDatabase *db = (*iter);
|
||||||
|
while (!db->Close())
|
||||||
|
{
|
||||||
|
/* Spool until it closes */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyDriver::Shutdown()
|
||||||
|
{
|
||||||
|
List<MyDatabase *>::iterator iter;
|
||||||
|
CloseDBList(m_PermDbs);
|
||||||
|
|
||||||
|
if (m_MyHandle != BAD_HANDLE)
|
||||||
|
{
|
||||||
|
dbi->ReleaseHandle(m_MyHandle, DBHandle_Driver, myself->GetIdentity());
|
||||||
|
m_MyHandle = BAD_HANDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *MyDriver::GetIdentifier()
|
||||||
|
{
|
||||||
|
return "mysql";
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle_t MyDriver::GetHandle()
|
||||||
|
{
|
||||||
|
if (m_MyHandle == BAD_HANDLE)
|
||||||
|
{
|
||||||
|
m_MyHandle = dbi->CreateHandle(DBHandle_Driver, this, myself->GetIdentity());
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_MyHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityToken_t *MyDriver::GetIdentity()
|
||||||
|
{
|
||||||
|
return myself->GetIdentity();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *MyDriver::GetProductName()
|
||||||
|
{
|
||||||
|
return "MySQL";
|
||||||
|
}
|
||||||
|
|
||||||
|
MYSQL *Connect(const DatabaseInfo *info, char *error, size_t maxlength)
|
||||||
|
{
|
||||||
|
MYSQL *mysql = mysql_init(NULL);
|
||||||
|
|
||||||
|
if (info->maxTimeout > 0)
|
||||||
|
{
|
||||||
|
mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&(info->maxTimeout));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mysql_real_connect(mysql,
|
||||||
|
info->host,
|
||||||
|
info->user,
|
||||||
|
info->pass,
|
||||||
|
info->database,
|
||||||
|
info->port,
|
||||||
|
NULL,
|
||||||
|
M_CLIENT_MULTI_RESULTS))
|
||||||
|
{
|
||||||
|
/* :TODO: expose UTIL_Format from smutil! */
|
||||||
|
snprintf(error, maxlength, "[%d]: %s", mysql_errno(mysql), mysql_error(mysql));
|
||||||
|
mysql_close(mysql);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mysql;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CompareField(const char *str1, const char *str2)
|
||||||
|
{
|
||||||
|
if ((str1 == NULL && str2 != NULL)
|
||||||
|
|| (str1 != NULL && str2 == NULL))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str1 == NULL && str2 == NULL)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (strcmp(str1, str2) == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
IDatabase *MyDriver::Connect(const DatabaseInfo *info, bool persistent, char *error, size_t maxlength)
|
||||||
|
{
|
||||||
|
if (persistent)
|
||||||
|
{
|
||||||
|
/* Try to find a matching persistent connection */
|
||||||
|
List<MyDatabase *>::iterator iter;
|
||||||
|
for (iter=m_PermDbs.begin();
|
||||||
|
iter!=m_PermDbs.end();
|
||||||
|
iter++)
|
||||||
|
{
|
||||||
|
MyDatabase *db = (*iter);
|
||||||
|
const DatabaseInfo &other = db->GetInfo();
|
||||||
|
if (CompareField(info->host, other.host)
|
||||||
|
&& CompareField(info->user, other.user)
|
||||||
|
&& CompareField(info->pass, other.pass)
|
||||||
|
&& CompareField(info->database, other.database)
|
||||||
|
&& (info->port == other.port))
|
||||||
|
{
|
||||||
|
db->IncRefCount();
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MYSQL *mysql = ::Connect(info, error, maxlength);
|
||||||
|
if (!mysql)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
MyDatabase *db = new MyDatabase(mysql, info, persistent);
|
||||||
|
|
||||||
|
if (persistent)
|
||||||
|
{
|
||||||
|
m_PermDbs.push_back(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyDriver::RemoveFromList(MyDatabase *pdb, bool persistent)
|
||||||
|
{
|
||||||
|
if (persistent)
|
||||||
|
{
|
||||||
|
m_PermDbs.remove(pdb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int strncopy(char *dest, const char *src, size_t count)
|
||||||
|
{
|
||||||
|
if (!count)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *start = dest;
|
||||||
|
while ((*src) && (--count))
|
||||||
|
{
|
||||||
|
*dest++ = *src++;
|
||||||
|
}
|
||||||
|
*dest = '\0';
|
||||||
|
|
||||||
|
return (dest - start);
|
||||||
|
}
|
45
extensions/mysql/mysql/MyDriver.h
Normal file
45
extensions/mysql/mysql/MyDriver.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#ifndef _INCLUDE_SM_MYSQL_DRIVER_H_
|
||||||
|
#define _INCLUDE_SM_MYSQL_DRIVER_H_
|
||||||
|
|
||||||
|
#include <IDBDriver.h>
|
||||||
|
#include <sm_platform.h>
|
||||||
|
#if defined PLATFORM_WINDOWS
|
||||||
|
#include <winsock.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <mysql.h>
|
||||||
|
|
||||||
|
#include <sh_string.h>
|
||||||
|
#include <sh_list.h>
|
||||||
|
|
||||||
|
using namespace SourceMod;
|
||||||
|
using namespace SourceHook;
|
||||||
|
|
||||||
|
#define M_CLIENT_MULTI_RESULTS ((1) << 17) /* Enable/disable multi-results */
|
||||||
|
|
||||||
|
class MyDatabase;
|
||||||
|
|
||||||
|
class MyDriver : public IDBDriver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MyDriver();
|
||||||
|
public: //IDBDriver
|
||||||
|
IDatabase *Connect(const DatabaseInfo *info, bool persistent, char *error, size_t maxlength);
|
||||||
|
const char *GetIdentifier();
|
||||||
|
const char *GetProductName();
|
||||||
|
Handle_t GetHandle();
|
||||||
|
IdentityToken_t *GetIdentity();
|
||||||
|
public:
|
||||||
|
void Shutdown();
|
||||||
|
void RemoveFromList(MyDatabase *pdb, bool persistent);
|
||||||
|
private:
|
||||||
|
Handle_t m_MyHandle;
|
||||||
|
List<MyDatabase *> m_TempDbs;
|
||||||
|
List<MyDatabase *> m_PermDbs;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern MyDriver g_MyDriver;
|
||||||
|
|
||||||
|
unsigned int strncopy(char *dest, const char *src, size_t count);
|
||||||
|
|
||||||
|
#endif //_INCLUDE_SM_MYSQL_DRIVER_H_
|
7
extensions/mysql/mysql/MyQuery.cpp
Normal file
7
extensions/mysql/mysql/MyQuery.cpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#include "MyQuery.h"
|
||||||
|
|
||||||
|
MyQuery::MyQuery(MyDatabase *db, MYSQL_RES *res)
|
||||||
|
: m_pParent(db)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
26
extensions/mysql/mysql/MyQuery.h
Normal file
26
extensions/mysql/mysql/MyQuery.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef _INCLUDE_SM_MYSQL_QUERY_H_
|
||||||
|
#define _INCLUDE_SM_MYSQL_QUERY_H_
|
||||||
|
|
||||||
|
#include "MyDriver.h"
|
||||||
|
#include "MyDatabase.h"
|
||||||
|
|
||||||
|
class MyResultSet :
|
||||||
|
public IResultSet,
|
||||||
|
public IResultRow
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyQuery : public IQuery
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MyQuery(MyDatabase *db, MYSQL_RES *res);
|
||||||
|
public:
|
||||||
|
IResultSet *GetResults();
|
||||||
|
void Destroy();
|
||||||
|
private:
|
||||||
|
MyDatabase *m_pParent;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //_INCLUDE_SM_MYSQL_QUERY_H_
|
257
extensions/mysql/mysql/MyStatement.cpp
Normal file
257
extensions/mysql/mysql/MyStatement.cpp
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
#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->IncRefCount();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
51
extensions/mysql/mysql/MyStatement.h
Normal file
51
extensions/mysql/mysql/MyStatement.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#ifndef _INCLUDE_SM_MYSQL_STATEMENT_H_
|
||||||
|
#define _INCLUDE_SM_MYSQL_STATEMENT_H_
|
||||||
|
|
||||||
|
#include "MyDatabase.h"
|
||||||
|
#include "MyBoundResults.h"
|
||||||
|
|
||||||
|
struct ParamBind
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
float fval;
|
||||||
|
int ival;
|
||||||
|
} data;
|
||||||
|
void *blob;
|
||||||
|
size_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyStatement : public IPreparedQuery
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MyStatement(MyDatabase *db, MYSQL_STMT *stmt);
|
||||||
|
~MyStatement();
|
||||||
|
public: //IQuery
|
||||||
|
IResultSet *GetResultSet();
|
||||||
|
bool FetchMoreResults();
|
||||||
|
void Destroy();
|
||||||
|
public: //IPreparedQuery
|
||||||
|
bool BindParamInt(unsigned int param, int num, bool signd=true);
|
||||||
|
bool BindParamFloat(unsigned int param, float f);
|
||||||
|
bool BindParamNull(unsigned int param);
|
||||||
|
bool BindParamString(unsigned int param, const char *text, bool copy);
|
||||||
|
bool BindParamBlob(unsigned int param, const void *data, size_t length, bool copy);
|
||||||
|
bool Execute();
|
||||||
|
const char *GetError(int *errCode=NULL);
|
||||||
|
unsigned int GetAffectedRows();
|
||||||
|
unsigned int GetInsertID();
|
||||||
|
private:
|
||||||
|
void *CopyBlob(unsigned int param, const void *blobptr, size_t length);
|
||||||
|
private:
|
||||||
|
MYSQL *m_mysql;
|
||||||
|
MYSQL_STMT *m_stmt;
|
||||||
|
MYSQL_BIND *m_bind;
|
||||||
|
MYSQL_RES *m_pRes;
|
||||||
|
MyDatabase *m_pParent;
|
||||||
|
ParamBind *m_pushinfo;
|
||||||
|
unsigned int m_Params;
|
||||||
|
MyBoundResults *m_rs;
|
||||||
|
bool m_Results;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //_INCLUDE_SM_MYSQL_STATEMENT_H_
|
54
extensions/mysql/sdk/smsdk_config.h
Normal file
54
extensions/mysql/sdk/smsdk_config.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* vim: set ts=4 :
|
||||||
|
* ===============================================================
|
||||||
|
* SourceMod, Copyright (C) 2004-2007 AlliedModders LLC.
|
||||||
|
* All rights reserved.
|
||||||
|
* ===============================================================
|
||||||
|
*
|
||||||
|
* This file is part of the SourceMod/SourcePawn SDK. This file may only be
|
||||||
|
* used or modified under the Terms and Conditions of its License Agreement,
|
||||||
|
* which is found in public/licenses/LICENSE.txt. As of this notice, derivative
|
||||||
|
* works must be licensed under the GNU General Public License (version 2 or
|
||||||
|
* greater). A copy of the GPL is included under public/licenses/GPL.txt.
|
||||||
|
*
|
||||||
|
* To view the latest information, see: http://www.sourcemod.net/license.php
|
||||||
|
*
|
||||||
|
* Version: $Id: smsdk_config.h 763 2007-05-09 05:20:03Z damagedsoul $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
|
||||||
|
#define _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file smsdk_config.h
|
||||||
|
* @brief Contains macros for configuring basic extension information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Basic information exposed publicly */
|
||||||
|
#define SMEXT_CONF_NAME "MySQL-DBI"
|
||||||
|
#define SMEXT_CONF_DESCRIPTION "MySQL driver implementation for DBI"
|
||||||
|
#define SMEXT_CONF_VERSION "1.0.0.0"
|
||||||
|
#define SMEXT_CONF_AUTHOR "AlliedModders"
|
||||||
|
#define SMEXT_CONF_URL "http://www.sourcemod.net/"
|
||||||
|
#define SMEXT_CONF_LOGTAG "MYSQL"
|
||||||
|
#define SMEXT_CONF_LICENSE "GPL"
|
||||||
|
#define SMEXT_CONF_DATESTRING __DATE__
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Exposes plugin's main interface.
|
||||||
|
*/
|
||||||
|
#define SMEXT_LINK(name) SDKExtension *g_pExtensionIface = name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets whether or not this plugin required Metamod.
|
||||||
|
* NOTE: Uncomment to enable, comment to disable.
|
||||||
|
*/
|
||||||
|
//#define SMEXT_CONF_METAMOD
|
||||||
|
|
||||||
|
/** Enable interfaces you want to use here by uncommenting lines */
|
||||||
|
//#define SMEXT_ENABLE_FORWARDSYS
|
||||||
|
//#define SMEXT_ENABLE_HANDLESYS
|
||||||
|
//#define SMEXT_ENABLE_PLAYERHELPERS
|
||||||
|
#define SMEXT_ENABLE_DBMANAGER
|
||||||
|
|
||||||
|
#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
|
361
extensions/mysql/sdk/smsdk_ext.cpp
Normal file
361
extensions/mysql/sdk/smsdk_ext.cpp
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
/**
|
||||||
|
* vim: set ts=4 :
|
||||||
|
* ===============================================================
|
||||||
|
* SourceMod, Copyright (C) 2004-2007 AlliedModders LLC.
|
||||||
|
* All rights reserved.
|
||||||
|
* ===============================================================
|
||||||
|
*
|
||||||
|
* This file is part of the SourceMod/SourcePawn SDK. This file may only be
|
||||||
|
* used or modified under the Terms and Conditions of its License Agreement,
|
||||||
|
* which is found in public/licenses/LICENSE.txt. As of this notice, derivative
|
||||||
|
* works must be licensed under the GNU General Public License (version 2 or
|
||||||
|
* greater). A copy of the GPL is included under public/licenses/GPL.txt.
|
||||||
|
*
|
||||||
|
* To view the latest information, see: http://www.sourcemod.net/license.php
|
||||||
|
*
|
||||||
|
* Version: $Id: smsdk_ext.cpp 763 2007-05-09 05:20:03Z damagedsoul $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include "smsdk_ext.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file smsdk_ext.cpp
|
||||||
|
* @brief Contains wrappers for making Extensions easier to write.
|
||||||
|
*/
|
||||||
|
|
||||||
|
IExtension *myself = NULL; /**< Ourself */
|
||||||
|
IShareSys *g_pShareSys = NULL; /**< Share system */
|
||||||
|
IShareSys *sharesys = NULL; /**< Share system */
|
||||||
|
ISourceMod *g_pSM = NULL; /**< SourceMod helpers */
|
||||||
|
ISourceMod *smutils = NULL; /**< SourceMod helpers */
|
||||||
|
|
||||||
|
#if defined SMEXT_ENABLE_FORWARDSYS
|
||||||
|
IForwardManager *g_pForwards = NULL; /**< Forward system */
|
||||||
|
IForwardManager *forwards = NULL; /**< Forward system */
|
||||||
|
#endif
|
||||||
|
#if defined SMEXT_ENABLE_HANDLESYS
|
||||||
|
IHandleSys *g_pHandleSys = NULL; /**< Handle system */
|
||||||
|
IHandleSys *handlesys = NULL; /**< Handle system */
|
||||||
|
#endif
|
||||||
|
#if defined SMEXT_ENABLE_PLAYERHELPERS
|
||||||
|
IPlayerHelpers *playerhelpers = NULL; /**< Player helpers */
|
||||||
|
#endif //SMEXT_ENABLE_PLAYERHELPERS
|
||||||
|
#if defined SMEXT_ENABLE_DBMANAGER
|
||||||
|
IDBManager *dbi = NULL; /**< DB Manager */
|
||||||
|
#endif //SMEXT_ENABLE_DBMANAGER
|
||||||
|
|
||||||
|
/** Exports the main interface */
|
||||||
|
PLATFORM_EXTERN_C IExtensionInterface *GetSMExtAPI()
|
||||||
|
{
|
||||||
|
return g_pExtensionIface;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDKExtension::SDKExtension()
|
||||||
|
{
|
||||||
|
#if defined SMEXT_CONF_METAMOD
|
||||||
|
m_SourceMMLoaded = false;
|
||||||
|
m_WeAreUnloaded = false;
|
||||||
|
m_WeGotPauseChange = false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDKExtension::OnExtensionLoad(IExtension *me, IShareSys *sys, char *error, size_t maxlength, bool late)
|
||||||
|
{
|
||||||
|
g_pShareSys = sharesys = sys;
|
||||||
|
myself = me;
|
||||||
|
|
||||||
|
#if defined SMEXT_CONF_METAMOD
|
||||||
|
m_WeAreUnloaded = true;
|
||||||
|
|
||||||
|
if (!m_SourceMMLoaded)
|
||||||
|
{
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
snprintf(error, maxlength, "Metamod attach failed");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
SM_GET_IFACE(SOURCEMOD, g_pSM);
|
||||||
|
smutils = g_pSM;
|
||||||
|
#if defined SMEXT_ENABLE_HANDLESYS
|
||||||
|
SM_GET_IFACE(HANDLESYSTEM, g_pHandleSys);
|
||||||
|
handlesys = g_pHandleSys;
|
||||||
|
#endif
|
||||||
|
#if defined SMEXT_ENABLE_FORWARDSYS
|
||||||
|
SM_GET_IFACE(FORWARDMANAGER, g_pForwards);
|
||||||
|
forwards = g_pForwards;
|
||||||
|
#endif
|
||||||
|
#if defined SMEXT_ENABLE_PLAYERHELPERS
|
||||||
|
SM_GET_IFACE(PLAYERMANAGER, playerhelpers);
|
||||||
|
#endif
|
||||||
|
#if defined SMEXT_ENABLE_DBMANAGER
|
||||||
|
SM_GET_IFACE(DBI, dbi);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (SDK_OnLoad(error, maxlength, late))
|
||||||
|
{
|
||||||
|
#if defined SMEXT_CONF_METAMOD
|
||||||
|
m_WeAreUnloaded = true;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDKExtension::IsMetamodExtension()
|
||||||
|
{
|
||||||
|
#if defined SMEXT_CONF_METAMOD
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDKExtension::OnExtensionPauseChange(bool state)
|
||||||
|
{
|
||||||
|
#if defined SMEXT_CONF_METAMOD
|
||||||
|
m_WeGotPauseChange = true;
|
||||||
|
#endif
|
||||||
|
SDK_OnPauseChange(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDKExtension::OnExtensionsAllLoaded()
|
||||||
|
{
|
||||||
|
SDK_OnAllLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDKExtension::OnExtensionUnload()
|
||||||
|
{
|
||||||
|
#if defined SMEXT_CONF_METAMOD
|
||||||
|
m_WeAreUnloaded = true;
|
||||||
|
#endif
|
||||||
|
SDK_OnUnload();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SDKExtension::GetExtensionAuthor()
|
||||||
|
{
|
||||||
|
return SMEXT_CONF_AUTHOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SDKExtension::GetExtensionDateString()
|
||||||
|
{
|
||||||
|
return SMEXT_CONF_DATESTRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SDKExtension::GetExtensionDescription()
|
||||||
|
{
|
||||||
|
return SMEXT_CONF_DESCRIPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SDKExtension::GetExtensionVerString()
|
||||||
|
{
|
||||||
|
return SMEXT_CONF_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SDKExtension::GetExtensionName()
|
||||||
|
{
|
||||||
|
return SMEXT_CONF_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SDKExtension::GetExtensionTag()
|
||||||
|
{
|
||||||
|
return SMEXT_CONF_LOGTAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SDKExtension::GetExtensionURL()
|
||||||
|
{
|
||||||
|
return SMEXT_CONF_URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDKExtension::SDK_OnLoad(char *error, size_t maxlength, bool late)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDKExtension::SDK_OnUnload()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDKExtension::SDK_OnPauseChange(bool paused)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDKExtension::SDK_OnAllLoaded()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined SMEXT_CONF_METAMOD
|
||||||
|
|
||||||
|
PluginId g_PLID = 0; /**< Metamod plugin ID */
|
||||||
|
ISmmPlugin *g_PLAPI = NULL; /**< Metamod plugin API */
|
||||||
|
SourceHook::ISourceHook *g_SHPtr = NULL; /**< SourceHook pointer */
|
||||||
|
ISmmAPI *g_SMAPI = NULL; /**< SourceMM API pointer */
|
||||||
|
|
||||||
|
IVEngineServer *engine = NULL; /**< IVEngineServer pointer */
|
||||||
|
IServerGameDLL *gamedll = NULL; /**< IServerGameDLL pointer */
|
||||||
|
|
||||||
|
/** Exposes the extension to Metamod */
|
||||||
|
SMM_API void *PL_EXPOSURE(const char *name, int *code)
|
||||||
|
{
|
||||||
|
if (name && !strcmp(name, PLAPI_NAME))
|
||||||
|
{
|
||||||
|
if (code)
|
||||||
|
{
|
||||||
|
*code = IFACE_OK;
|
||||||
|
}
|
||||||
|
return static_cast<void *>(g_pExtensionIface);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code)
|
||||||
|
{
|
||||||
|
*code = IFACE_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDKExtension::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late)
|
||||||
|
{
|
||||||
|
PLUGIN_SAVEVARS();
|
||||||
|
|
||||||
|
GET_V_IFACE_ANY(serverFactory, gamedll, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL);
|
||||||
|
GET_V_IFACE_CURRENT(engineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER);
|
||||||
|
|
||||||
|
m_SourceMMLoaded = true;
|
||||||
|
|
||||||
|
return SDK_OnMetamodLoad(ismm, error, maxlen, late);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDKExtension::Unload(char *error, size_t maxlen)
|
||||||
|
{
|
||||||
|
if (!m_WeAreUnloaded)
|
||||||
|
{
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
snprintf(error, maxlen, "This extension must be unloaded by SourceMod.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDK_OnMetamodUnload(error, maxlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDKExtension::Pause(char *error, size_t maxlen)
|
||||||
|
{
|
||||||
|
if (!m_WeGotPauseChange)
|
||||||
|
{
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
snprintf(error, maxlen, "This extension must be paused by SourceMod.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_WeGotPauseChange = false;
|
||||||
|
|
||||||
|
return SDK_OnMetamodPauseChange(true, error, maxlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDKExtension::Unpause(char *error, size_t maxlen)
|
||||||
|
{
|
||||||
|
if (!m_WeGotPauseChange)
|
||||||
|
{
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
snprintf(error, maxlen, "This extension must be unpaused by SourceMod.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_WeGotPauseChange = false;
|
||||||
|
|
||||||
|
return SDK_OnMetamodPauseChange(false, error, maxlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SDKExtension::GetAuthor()
|
||||||
|
{
|
||||||
|
return GetExtensionAuthor();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SDKExtension::GetDate()
|
||||||
|
{
|
||||||
|
return GetExtensionDateString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SDKExtension::GetDescription()
|
||||||
|
{
|
||||||
|
return GetExtensionDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SDKExtension::GetLicense()
|
||||||
|
{
|
||||||
|
return SMEXT_CONF_LICENSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SDKExtension::GetLogTag()
|
||||||
|
{
|
||||||
|
return GetExtensionTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SDKExtension::GetName()
|
||||||
|
{
|
||||||
|
return GetExtensionName();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SDKExtension::GetURL()
|
||||||
|
{
|
||||||
|
return GetExtensionURL();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SDKExtension::GetVersion()
|
||||||
|
{
|
||||||
|
return GetExtensionVerString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDKExtension::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDKExtension::SDK_OnMetamodUnload(char *error, size_t maxlength)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDKExtension::SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Overload a few things to prevent libstdc++ linking */
|
||||||
|
#if defined __linux__
|
||||||
|
extern "C" void __cxa_pure_virtual(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void *operator new(size_t size)
|
||||||
|
{
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *operator new[](size_t size)
|
||||||
|
{
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator delete(void *ptr)
|
||||||
|
{
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator delete[](void * ptr)
|
||||||
|
{
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
#endif
|
237
extensions/mysql/sdk/smsdk_ext.h
Normal file
237
extensions/mysql/sdk/smsdk_ext.h
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
/**
|
||||||
|
* vim: set ts=4 :
|
||||||
|
* ===============================================================
|
||||||
|
* SourceMod, Copyright (C) 2004-2007 AlliedModders LLC.
|
||||||
|
* All rights reserved.
|
||||||
|
* ===============================================================
|
||||||
|
*
|
||||||
|
* This file is part of the SourceMod/SourcePawn SDK. This file may only be
|
||||||
|
* used or modified under the Terms and Conditions of its License Agreement,
|
||||||
|
* which is found in public/licenses/LICENSE.txt. As of this notice, derivative
|
||||||
|
* works must be licensed under the GNU General Public License (version 2 or
|
||||||
|
* greater). A copy of the GPL is included under public/licenses/GPL.txt.
|
||||||
|
*
|
||||||
|
* To view the latest information, see: http://www.sourcemod.net/license.php
|
||||||
|
*
|
||||||
|
* Version: $Id: smsdk_ext.h 763 2007-05-09 05:20:03Z damagedsoul $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _INCLUDE_SOURCEMOD_EXTENSION_BASESDK_H_
|
||||||
|
#define _INCLUDE_SOURCEMOD_EXTENSION_BASESDK_H_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file smsdk_ext.h
|
||||||
|
* @brief Contains wrappers for making Extensions easier to write.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "smsdk_config.h"
|
||||||
|
#include <IExtensionSys.h>
|
||||||
|
#include <IHandleSys.h>
|
||||||
|
#include <sp_vm_api.h>
|
||||||
|
#include <sm_platform.h>
|
||||||
|
#include <ISourceMod.h>
|
||||||
|
#if defined SMEXT_ENABLE_FORWARDSYS
|
||||||
|
#include <IForwardSys.h>
|
||||||
|
#endif //SMEXT_ENABLE_FORWARDSYS
|
||||||
|
#if defined SMEXT_ENABLE_PLAYERHELPERS
|
||||||
|
#include <IPlayerHelpers.h>
|
||||||
|
#endif //SMEXT_ENABLE_PlAYERHELPERS
|
||||||
|
#if defined SMEXT_ENABLE_DBMANAGER
|
||||||
|
#include <IDBDriver.h>
|
||||||
|
#endif //SMEXT_ENABLE_DBMANAGER
|
||||||
|
|
||||||
|
#if defined SMEXT_CONF_METAMOD
|
||||||
|
#include <ISmmPlugin.h>
|
||||||
|
#include <eiface.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace SourceMod;
|
||||||
|
using namespace SourcePawn;
|
||||||
|
|
||||||
|
class SDKExtension :
|
||||||
|
#if defined SMEXT_CONF_METAMOD
|
||||||
|
public ISmmPlugin,
|
||||||
|
#endif
|
||||||
|
public IExtensionInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** Constructor */
|
||||||
|
SDKExtension();
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief This is called after the initial loading sequence has been processed.
|
||||||
|
*
|
||||||
|
* @param error Error message buffer.
|
||||||
|
* @param maxlength Size of error message buffer.
|
||||||
|
* @param late Whether or not the module was loaded after map load.
|
||||||
|
* @return True to succeed loading, false to fail.
|
||||||
|
*/
|
||||||
|
virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is called right before the extension is unloaded.
|
||||||
|
*/
|
||||||
|
virtual void SDK_OnUnload();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is called once all known extensions have been loaded.
|
||||||
|
*/
|
||||||
|
virtual void SDK_OnAllLoaded();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called when the pause state is changed.
|
||||||
|
*/
|
||||||
|
virtual void SDK_OnPauseChange(bool paused);
|
||||||
|
|
||||||
|
#if defined SMEXT_CONF_METAMOD
|
||||||
|
/**
|
||||||
|
* @brief Called when Metamod is attached, before the extension version is called.
|
||||||
|
*
|
||||||
|
* @param error Error buffer.
|
||||||
|
* @param maxlength Maximum size of error buffer.
|
||||||
|
* @param late Whether or not Metamod considers this a late load.
|
||||||
|
* @return True to succeed, false to fail.
|
||||||
|
*/
|
||||||
|
virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called when Metamod is detaching, after the extension version is called.
|
||||||
|
* NOTE: By default this is blocked unless sent from SourceMod.
|
||||||
|
*
|
||||||
|
* @param error Error buffer.
|
||||||
|
* @param maxlength Maximum size of error buffer.
|
||||||
|
* @return True to succeed, false to fail.
|
||||||
|
*/
|
||||||
|
virtual bool SDK_OnMetamodUnload(char *error, size_t maxlength);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called when Metamod's pause state is changing.
|
||||||
|
* NOTE: By default this is blocked unless sent from SourceMod.
|
||||||
|
*
|
||||||
|
* @param paused Pause state being set.
|
||||||
|
* @param error Error buffer.
|
||||||
|
* @param maxlength Maximum size of error buffer.
|
||||||
|
* @return True to succeed, false to fail.
|
||||||
|
*/
|
||||||
|
virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public: //IExtensionInterface
|
||||||
|
virtual bool OnExtensionLoad(IExtension *me, IShareSys *sys, char *error, size_t maxlength, bool late);
|
||||||
|
virtual void OnExtensionUnload();
|
||||||
|
virtual void OnExtensionsAllLoaded();
|
||||||
|
|
||||||
|
/** Returns whether or not this is a Metamod-based extension */
|
||||||
|
virtual bool IsMetamodExtension();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called when the pause state changes.
|
||||||
|
*
|
||||||
|
* @param state True if being paused, false if being unpaused.
|
||||||
|
*/
|
||||||
|
virtual void OnExtensionPauseChange(bool state);
|
||||||
|
|
||||||
|
/** Returns name */
|
||||||
|
virtual const char *GetExtensionName();
|
||||||
|
/** Returns URL */
|
||||||
|
virtual const char *GetExtensionURL();
|
||||||
|
/** Returns log tag */
|
||||||
|
virtual const char *GetExtensionTag();
|
||||||
|
/** Returns author */
|
||||||
|
virtual const char *GetExtensionAuthor();
|
||||||
|
/** Returns version string */
|
||||||
|
virtual const char *GetExtensionVerString();
|
||||||
|
/** Returns description string */
|
||||||
|
virtual const char *GetExtensionDescription();
|
||||||
|
/** Returns date string */
|
||||||
|
virtual const char *GetExtensionDateString();
|
||||||
|
#if defined SMEXT_CONF_METAMOD
|
||||||
|
public: //ISmmPlugin
|
||||||
|
/** Called when the extension is attached to Metamod. */
|
||||||
|
virtual bool Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlength, bool late);
|
||||||
|
/** Returns the author to MM */
|
||||||
|
virtual const char *GetAuthor();
|
||||||
|
/** Returns the name to MM */
|
||||||
|
virtual const char *GetName();
|
||||||
|
/** Returns the description to MM */
|
||||||
|
virtual const char *GetDescription();
|
||||||
|
/** Returns the URL to MM */
|
||||||
|
virtual const char *GetURL();
|
||||||
|
/** Returns the license to MM */
|
||||||
|
virtual const char *GetLicense();
|
||||||
|
/** Returns the version string to MM */
|
||||||
|
virtual const char *GetVersion();
|
||||||
|
/** Returns the date string to MM */
|
||||||
|
virtual const char *GetDate();
|
||||||
|
/** Returns the logtag to MM */
|
||||||
|
virtual const char *GetLogTag();
|
||||||
|
/** Called on unload */
|
||||||
|
virtual bool Unload(char *error, size_t maxlength);
|
||||||
|
/** Called on pause */
|
||||||
|
virtual bool Pause(char *error, size_t maxlength);
|
||||||
|
/** Called on unpause */
|
||||||
|
virtual bool Unpause(char *error, size_t maxlength);
|
||||||
|
private:
|
||||||
|
bool m_SourceMMLoaded;
|
||||||
|
bool m_WeAreUnloaded;
|
||||||
|
bool m_WeGotPauseChange;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
extern SDKExtension *g_pExtensionIface;
|
||||||
|
extern IExtension *myself;
|
||||||
|
|
||||||
|
extern IShareSys *g_pShareSys;
|
||||||
|
extern IShareSys *sharesys; /* Note: Newer name */
|
||||||
|
extern ISourceMod *g_pSM;
|
||||||
|
extern ISourceMod *smutils; /* Note: Newer name */
|
||||||
|
|
||||||
|
/* Optional interfaces are below */
|
||||||
|
#if defined SMEXT_ENABLE_FORWARDSYS
|
||||||
|
extern IForwardManager *g_pForwards;
|
||||||
|
extern IForwardManager *forwards; /* Note: Newer name */
|
||||||
|
#endif //SMEXT_ENABLE_FORWARDSYS
|
||||||
|
#if defined SMEXT_ENABLE_HANDLESYS
|
||||||
|
extern IHandleSys *g_pHandleSys;
|
||||||
|
extern IHandleSys *handlesys; /* Note: Newer name */
|
||||||
|
#endif //SMEXT_ENABLE_HANDLESYS
|
||||||
|
#if defined SMEXT_ENABLE_PLAYERHELPERS
|
||||||
|
extern IPlayerHelpers *playerhelpers;
|
||||||
|
#endif //SMEXT_ENABLE_PLAYERHELPERS
|
||||||
|
#if defined SMEXT_ENABLE_DBMANAGER
|
||||||
|
extern IDBManager *dbi;
|
||||||
|
#endif //SMEXT_ENABLE_DBMANAGER
|
||||||
|
|
||||||
|
#if defined SMEXT_CONF_METAMOD
|
||||||
|
PLUGIN_GLOBALVARS();
|
||||||
|
extern IVEngineServer *engine;
|
||||||
|
extern IServerGameDLL *gamedll;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Creates a SourceMod interface macro pair */
|
||||||
|
#define SM_MKIFACE(name) SMINTERFACE_##name##_NAME, SMINTERFACE_##name##_VERSION
|
||||||
|
/** Automates retrieving SourceMod interfaces */
|
||||||
|
#define SM_GET_IFACE(prefix, addr) \
|
||||||
|
if (!g_pShareSys->RequestInterface(SM_MKIFACE(prefix), myself, (SMInterface **)&addr)) \
|
||||||
|
{ \
|
||||||
|
if (error) \
|
||||||
|
{ \
|
||||||
|
snprintf(error, maxlength, "Could not find interface: %s (version: %d)", SMINTERFACE_##prefix##_NAME, SMINTERFACE_##prefix##_VERSION); \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
/** Automates retrieving SourceMod interfaces when needed outside of SDK_OnLoad() */
|
||||||
|
#define SM_GET_LATE_IFACE(prefix, addr) \
|
||||||
|
g_pShareSys->RequestInterface(SM_MKIFACE(prefix), myself, (SMInterface **)&addr)
|
||||||
|
/** Validates a SourceMod interface pointer */
|
||||||
|
#define SM_CHECK_IFACE(prefix, addr) \
|
||||||
|
if (!addr) \
|
||||||
|
{ \
|
||||||
|
if (error) \
|
||||||
|
{ \
|
||||||
|
snprintf(error, maxlength, "Could not find interface: %s", SMINTERFACE_##prefix##_NAME); \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _INCLUDE_SOURCEMOD_EXTENSION_BASESDK_H_
|
@ -224,6 +224,14 @@ namespace SourceMod
|
|||||||
*/
|
*/
|
||||||
virtual IResultRow *FetchRow() =0;
|
virtual IResultRow *FetchRow() =0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a pointer to the current row.
|
||||||
|
*
|
||||||
|
* @return IResultRow pointer to the current row,
|
||||||
|
* or NULL if the current row is invalid.
|
||||||
|
*/
|
||||||
|
virtual IResultRow *CurrentRow() =0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Rewinds back to the beginning of the row iteration.
|
* @brief Rewinds back to the beginning of the row iteration.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user