| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| /** | ||
| * vim: set ts=4 : | ||
| * ============================================================================= | ||
| * SourceMod PostgreSQL Extension | ||
| * Copyright (C) 2013 AlliedModders LLC. All rights reserved. | ||
| * ============================================================================= | ||
| * | ||
| * This program is free software; you can redistribute it and/or modify it under | ||
| * the terms of the GNU General Public License, version 3.0, as published by the | ||
| * Free Software Foundation. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
| * details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License along with | ||
| * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| * | ||
| * As a special exception, AlliedModders LLC gives you permission to link the | ||
| * code of this program (as well as its derivative works) to "Half-Life 2," the | ||
| * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software | ||
| * by the Valve Corporation. You must obey the GNU General Public License in | ||
| * all respects for all other code used. Additionally, AlliedModders LLC grants | ||
| * this exception to all derivative works. AlliedModders LLC defines further | ||
| * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), | ||
| * or <http://www.sourcemod.net/license.php>. | ||
| * | ||
| * Version: $Id$ | ||
| */ | ||
|
|
||
| #ifndef _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ | ||
| #define _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ | ||
|
|
||
| /** | ||
| * @file extension.h | ||
| * @brief Sample extension code header. | ||
| */ | ||
|
|
||
| #define SOURCEMOD_SQL_DRIVER_CODE | ||
| #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_PgSQL : 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); | ||
| const char *GetExtensionVerString(); | ||
| const char *GetExtensionDateString(); | ||
| 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_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| /* src/include/pg_config_ext.h. Generated from pg_config_ext.h.in by configure. */ | ||
| /* | ||
| * src/include/pg_config_ext.h.in. This is generated manually, not by | ||
| * autoheader, since we want to limit which symbols get defined here. | ||
| */ | ||
|
|
||
| /* Define to the name of a signed 64-bit integer type. */ | ||
| #define PG_INT64_TYPE long long int |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| /* src/include/pg_config_ext.h. Generated from pg_config_ext.h.in by configure. */ | ||
| /* | ||
| * src/include/pg_config_ext.h.in. This is generated manually, not by | ||
| * autoheader, since we want to limit which symbols get defined here. | ||
| */ | ||
|
|
||
| /* Define to the name of a signed 64-bit integer type. */ | ||
| #define PG_INT64_TYPE long int |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| /* src/include/pg_config_ext.h. Generated from pg_config_ext.h.in by configure. */ | ||
| /* | ||
| * src/include/pg_config_ext.h.in. This is generated manually, not by | ||
| * autoheader, since we want to limit which symbols get defined here. | ||
| */ | ||
|
|
||
| /* Define to the name of a signed 64-bit integer type. */ | ||
| #define PG_INT64_TYPE long long int |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| /* src/include/pg_config_ext.h. Generated from pg_config_ext.h.in by configure. */ | ||
| /* | ||
| * src/include/pg_config_ext.h.in. This is generated manually, not by | ||
| * autoheader, since we want to limit which symbols get defined here. | ||
| */ | ||
|
|
||
| /* Define to the name of a signed 64-bit integer type. */ | ||
| #define PG_INT64_TYPE long int |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| /* | ||
| * src/include/pg_config_ext.h.win32. This is generated manually, not by | ||
| * autoheader, since we want to limit which symbols get defined here. | ||
| */ | ||
|
|
||
| /* Define to the name of a signed 64-bit integer type. */ | ||
| #define PG_INT64_TYPE long long int |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| /* | ||
| * src/include/pg_config_ext.h.win32. This is generated manually, not by | ||
| * autoheader, since we want to limit which symbols get defined here. | ||
| */ | ||
|
|
||
| /* Define to the name of a signed 64-bit integer type. */ | ||
| #define PG_INT64_TYPE long long int |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,350 @@ | ||
| /** | ||
| * vim: set ts=4 : | ||
| * ============================================================================= | ||
| * SourceMod PostgreSQL Extension | ||
| * Copyright (C) 2013 AlliedModders LLC. All rights reserved. | ||
| * ============================================================================= | ||
| * | ||
| * This program is free software; you can redistribute it and/or modify it under | ||
| * the terms of the GNU General Public License, version 3.0, as published by the | ||
| * Free Software Foundation. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
| * details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License along with | ||
| * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| * | ||
| * As a special exception, AlliedModders LLC gives you permission to link the | ||
| * code of this program (as well as its derivative works) to "Half-Life 2," the | ||
| * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software | ||
| * by the Valve Corporation. You must obey the GNU General Public License in | ||
| * all respects for all other code used. Additionally, AlliedModders LLC grants | ||
| * this exception to all derivative works. AlliedModders LLC defines further | ||
| * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), | ||
| * or <http://www.sourcemod.net/license.php>. | ||
| * | ||
| * Version: $Id$ | ||
| */ | ||
| #include <stdlib.h> | ||
| #include "PgBasicResults.h" | ||
|
|
||
| PgBasicResults::PgBasicResults(PGresult *res) | ||
| : m_pRes(res) | ||
| { | ||
| Update(); | ||
| } | ||
|
|
||
| PgBasicResults::~PgBasicResults() | ||
| { | ||
| } | ||
|
|
||
| void PgBasicResults::Update() | ||
| { | ||
| if (m_pRes) | ||
| { | ||
| m_ColCount = (unsigned int)PQnfields(m_pRes); | ||
| m_RowCount = (unsigned int)PQntuples(m_pRes); | ||
| m_CurRow = 0; | ||
| m_RowFetched = false; | ||
| } | ||
| } | ||
|
|
||
| unsigned int PgBasicResults::GetRowCount() | ||
| { | ||
| return m_RowCount; | ||
| } | ||
|
|
||
| unsigned int PgBasicResults::GetFieldCount() | ||
| { | ||
| return m_ColCount; | ||
| } | ||
|
|
||
| bool PgBasicResults::FieldNameToNum(const char *name, unsigned int *columnId) | ||
| { | ||
| int id = PQfnumber(m_pRes, name); | ||
|
|
||
| if (id == -1) | ||
| return false; | ||
|
|
||
| *columnId = (unsigned int)id; | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| const char *PgBasicResults::FieldNumToName(unsigned int colId) | ||
| { | ||
| if (colId >= m_ColCount) | ||
| return nullptr; | ||
|
|
||
| return PQfname(m_pRes, colId); | ||
| } | ||
|
|
||
| bool PgBasicResults::MoreRows() | ||
| { | ||
| return ((!m_RowFetched && m_RowCount > 0) || (m_RowFetched && m_CurRow < m_RowCount-1)); | ||
| } | ||
|
|
||
| IResultRow *PgBasicResults::FetchRow() | ||
| { | ||
| // Rows start at 0. Start incrementing the current row when fetching the second time. | ||
| if (m_RowFetched) | ||
| m_CurRow++; | ||
|
|
||
| // Reached the end of the rows. | ||
| if (m_CurRow >= m_RowCount) | ||
| { | ||
| m_CurRow = m_RowCount; | ||
| return nullptr; | ||
| } | ||
|
|
||
| // We fetched some rows. | ||
| m_RowFetched = true; | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| IResultRow *PgBasicResults::CurrentRow() | ||
| { | ||
| if (!m_pRes | ||
| || !m_RowFetched | ||
| || m_CurRow >= m_RowCount) | ||
| { | ||
| return nullptr; | ||
| } | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| bool PgBasicResults::Rewind() | ||
| { | ||
| m_RowFetched = false; | ||
| m_CurRow = 0; | ||
| return true; | ||
| } | ||
|
|
||
| DBType PgBasicResults::GetFieldType(unsigned int field) | ||
| { | ||
| if (field >= m_ColCount) | ||
| { | ||
| return DBType_Unknown; | ||
| } | ||
|
|
||
| Oid fld = PQftype(m_pRes, field); | ||
| if (fld == InvalidOid) | ||
| { | ||
| return DBType_Unknown; | ||
| } | ||
|
|
||
| return GetOurType(fld); | ||
| } | ||
|
|
||
| DBType PgBasicResults::GetFieldDataType(unsigned int field) | ||
| { | ||
| if (field >= m_ColCount) | ||
| { | ||
| return DBType_Unknown; | ||
| } | ||
|
|
||
| if (PQfformat(m_pRes, field) == 1) | ||
| { | ||
| return DBType_Blob; | ||
| } else { | ||
| return DBType_String; | ||
| } | ||
| } | ||
|
|
||
| bool PgBasicResults::IsNull(unsigned int columnId) | ||
| { | ||
| if (columnId >= m_ColCount) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| return (PQgetisnull(m_pRes, m_CurRow, columnId) == 1); | ||
| } | ||
|
|
||
| DBResult PgBasicResults::GetString(unsigned int columnId, const char **pString, size_t *length) | ||
| { | ||
| if (columnId >= m_ColCount) | ||
| { | ||
| return DBVal_Error; | ||
| } else if (IsNull(columnId)) { | ||
| *pString = ""; | ||
| if (length) | ||
| { | ||
| *length = 0; | ||
| } | ||
| return DBVal_Null; | ||
| } | ||
|
|
||
| *pString = PQgetvalue(m_pRes, m_CurRow, columnId); | ||
|
|
||
| if (length) | ||
| { | ||
| *length = GetDataSize(columnId); | ||
| } | ||
|
|
||
| return DBVal_Data; | ||
| } | ||
|
|
||
| DBResult PgBasicResults::CopyString(unsigned int columnId, | ||
| char *buffer, | ||
| size_t maxlength, | ||
| size_t *written) | ||
| { | ||
| DBResult res; | ||
| const char *str; | ||
| if ((res=GetString(columnId, &str, nullptr)) == DBVal_Error) | ||
| { | ||
| return DBVal_Error; | ||
| } | ||
|
|
||
| size_t wr = strncopy(buffer, str, maxlength); | ||
| if (written) | ||
| { | ||
| *written = wr; | ||
| } | ||
|
|
||
| return res; | ||
| } | ||
|
|
||
| size_t PgBasicResults::GetDataSize(unsigned int columnId) | ||
| { | ||
| if (columnId >= m_ColCount) | ||
| { | ||
| return 0; | ||
| } | ||
|
|
||
| return (size_t)PQgetlength(m_pRes, m_CurRow, columnId); | ||
| } | ||
|
|
||
| DBResult PgBasicResults::GetFloat(unsigned int col, float *fval) | ||
| { | ||
| if (col >= m_ColCount) | ||
| { | ||
| return DBVal_Error; | ||
| } else if (IsNull(col)) { | ||
| *fval = 0.0f; | ||
| return DBVal_Null; | ||
| } | ||
|
|
||
| *fval = (float)atof(PQgetvalue(m_pRes, m_CurRow, col)); | ||
|
|
||
| return DBVal_Data; | ||
| } | ||
|
|
||
| DBResult PgBasicResults::GetInt(unsigned int col, int *val) | ||
| { | ||
| if (col >= m_ColCount) | ||
| { | ||
| return DBVal_Error; | ||
| } else if (IsNull(col)) { | ||
| *val = 0; | ||
| return DBVal_Null; | ||
| } | ||
|
|
||
| *val = atoi(PQgetvalue(m_pRes, m_CurRow, col)); | ||
|
|
||
| return DBVal_Data; | ||
| } | ||
|
|
||
| DBResult PgBasicResults::GetBlob(unsigned int col, const void **pData, size_t *length) | ||
| { | ||
| if (col >= m_ColCount) | ||
| { | ||
| return DBVal_Error; | ||
| } else if (IsNull(col)) { | ||
| *pData = nullptr; | ||
| if (length) | ||
| { | ||
| *length = 0; | ||
| } | ||
| return DBVal_Null; | ||
| } | ||
|
|
||
| *pData = PQgetvalue(m_pRes, m_CurRow, col); | ||
|
|
||
| if (length) | ||
| { | ||
| *length = GetDataSize(col); | ||
| } | ||
|
|
||
| return DBVal_Data; | ||
| } | ||
|
|
||
| DBResult PgBasicResults::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) | ||
| { | ||
| return DBVal_Null; | ||
| } | ||
|
|
||
| if (length > maxlength) | ||
| { | ||
| length = maxlength; | ||
| } | ||
|
|
||
| memcpy(buffer, addr, length); | ||
| if (written) | ||
| { | ||
| *written = length; | ||
| } | ||
|
|
||
| return res; | ||
| } | ||
|
|
||
| PgQuery::PgQuery(PgDatabase *db, PGresult *res) | ||
| : m_pParent(db), m_rs(res) | ||
| { | ||
| m_InsertID = (unsigned int)PQoidValue(res); | ||
| m_AffectedRows = atoi(PQcmdTuples(res)); | ||
| m_pParent->SetLastIDAndRows(m_InsertID, m_AffectedRows); | ||
| } | ||
|
|
||
| IResultSet *PgQuery::GetResultSet() | ||
| { | ||
| if (!m_rs.m_pRes) | ||
| { | ||
| return nullptr; | ||
| } | ||
|
|
||
| return &m_rs; | ||
| } | ||
|
|
||
| unsigned int PgQuery::GetInsertID() | ||
| { | ||
| return m_InsertID; | ||
| } | ||
|
|
||
| unsigned int PgQuery::GetAffectedRows() | ||
| { | ||
| return m_AffectedRows; | ||
| } | ||
|
|
||
| bool PgQuery::FetchMoreResults() | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| void PgQuery::Destroy() | ||
| { | ||
| if (m_rs.m_pRes != nullptr) | ||
| { | ||
| PQclear(m_rs.m_pRes); | ||
| } | ||
|
|
||
| /* Self destruct */ | ||
| delete this; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| /** | ||
| * vim: set ts=4 : | ||
| * ============================================================================= | ||
| * SourceMod PostgreSQL Extension | ||
| * Copyright (C) 2013 AlliedModders LLC. All rights reserved. | ||
| * ============================================================================= | ||
| * | ||
| * This program is free software; you can redistribute it and/or modify it under | ||
| * the terms of the GNU General Public License, version 3.0, as published by the | ||
| * Free Software Foundation. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
| * details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License along with | ||
| * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| * | ||
| * As a special exception, AlliedModders LLC gives you permission to link the | ||
| * code of this program (as well as its derivative works) to "Half-Life 2," the | ||
| * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software | ||
| * by the Valve Corporation. You must obey the GNU General Public License in | ||
| * all respects for all other code used. Additionally, AlliedModders LLC grants | ||
| * this exception to all derivative works. AlliedModders LLC defines further | ||
| * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), | ||
| * or <http://www.sourcemod.net/license.php>. | ||
| * | ||
| * Version: $Id$ | ||
| */ | ||
|
|
||
| #ifndef _INCLUDE_SM_PGSQL_BASIC_RESULTS_H_ | ||
| #define _INCLUDE_SM_PGSQL_BASIC_RESULTS_H_ | ||
|
|
||
| #include <amtl/am-refcounting.h> | ||
| #include "PgDatabase.h" | ||
|
|
||
| class PgQuery; | ||
| class PgStatement; | ||
|
|
||
| class PgBasicResults : | ||
| public IResultSet, | ||
| public IResultRow | ||
| { | ||
| friend class PgQuery; | ||
| friend class PgStatement; | ||
| public: | ||
| PgBasicResults(PGresult *res); | ||
| ~PgBasicResults(); | ||
| 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: | ||
| PGresult *m_pRes; | ||
| bool m_RowFetched; | ||
| unsigned int m_CurRow; | ||
| unsigned int m_ColCount; | ||
| unsigned int m_RowCount; | ||
| }; | ||
|
|
||
| class PgQuery : public IQuery | ||
| { | ||
| friend class PgBasicResults; | ||
| public: | ||
| PgQuery(PgDatabase *db, PGresult *res); | ||
| public: | ||
| IResultSet *GetResultSet(); | ||
| bool FetchMoreResults(); | ||
| void Destroy(); | ||
| public: // Used by the driver to implement GetInsertIDForQuery()/GetAffectedRowsForQuery() | ||
| unsigned int GetInsertID(); | ||
| unsigned int GetAffectedRows(); | ||
| private: | ||
| ke::RefPtr<PgDatabase> m_pParent; | ||
| PgBasicResults m_rs; | ||
| unsigned int m_InsertID; | ||
| unsigned int m_AffectedRows; | ||
| }; | ||
|
|
||
| #endif //_INCLUDE_SM_PGSQL_BASIC_RESULTS_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,346 @@ | ||
| /** | ||
| * vim: set ts=4 : | ||
| * ============================================================================= | ||
| * SourceMod PostgreSQL Extension | ||
| * Copyright (C) 2013 AlliedModders LLC. All rights reserved. | ||
| * ============================================================================= | ||
| * | ||
| * This program is free software; you can redistribute it and/or modify it under | ||
| * the terms of the GNU General Public License, version 3.0, as published by the | ||
| * Free Software Foundation. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
| * details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License along with | ||
| * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| * | ||
| * As a special exception, AlliedModders LLC gives you permission to link the | ||
| * code of this program (as well as its derivative works) to "Half-Life 2," the | ||
| * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software | ||
| * by the Valve Corporation. You must obey the GNU General Public License in | ||
| * all respects for all other code used. Additionally, AlliedModders LLC grants | ||
| * this exception to all derivative works. AlliedModders LLC defines further | ||
| * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), | ||
| * or <http://www.sourcemod.net/license.php>. | ||
| * | ||
| * Version: $Id$ | ||
| */ | ||
|
|
||
| #include "PgDatabase.h" | ||
| #include "smsdk_ext.h" | ||
| #include "PgBasicResults.h" | ||
| #include "PgStatement.h" | ||
|
|
||
| // Some selected defines from postgresql 9.2.4's src/include/catalog/pg_type.h | ||
| // Fast scan to extract the types that shouldn't be read as string. | ||
|
|
||
| // Floats | ||
| #define FLOAT4OID 700 | ||
| #define FLOAT8OID 701 | ||
| #define NUMERICOID 1700 | ||
|
|
||
| // Integer | ||
| #define BOOLOID 16 | ||
| #define INT8OID 20 | ||
| #define INT2OID 21 | ||
| #define INT4OID 23 | ||
| #define OIDOID 26 | ||
| #define TIDOID 27 | ||
| #define XIDOID 28 | ||
| #define CIDOID 29 | ||
|
|
||
| #define ABSTIMEOID 702 | ||
| #define RELTIMEOID 703 | ||
| #define TINTERVALOID 704 | ||
|
|
||
| #define REFCURSOROID 1790 | ||
|
|
||
| // String | ||
| #define BYTEAOID 17 // Hrmpf. Could be both, string or blob binary data.. | ||
| #define CHAROID 18 | ||
| #define NAMEOID 19 | ||
| #define TEXTOID 25 | ||
| #define BPCHAROID 1042 | ||
| #define VARCHAROID 1043 | ||
| #define DATEOID 1082 | ||
| #define TIMEOID 1083 | ||
|
|
||
| // Blob | ||
| #define POINTOID 600 | ||
| #define LSEGOID 601 | ||
| #define PATHOID 602 | ||
| #define BOXOID 603 | ||
| #define POLYGONOID 604 | ||
|
|
||
| DBType GetOurType(Oid type) | ||
| { | ||
| switch (type) | ||
| { | ||
| case FLOAT4OID: | ||
| case FLOAT8OID: | ||
| { | ||
| return DBType_Float; | ||
| } | ||
| case BOOLOID: | ||
| case INT8OID: | ||
| case INT2OID: | ||
| case INT4OID: | ||
| case OIDOID: | ||
| case TIDOID: | ||
| case XIDOID: | ||
| case CIDOID: | ||
| case ABSTIMEOID: | ||
| case RELTIMEOID: | ||
| case TINTERVALOID: | ||
| case REFCURSOROID: | ||
| { | ||
| return DBType_Integer; | ||
| } | ||
| case BYTEAOID: | ||
| case CHAROID: | ||
| case NAMEOID: | ||
| case TEXTOID: | ||
| case BPCHAROID: | ||
| case VARCHAROID: | ||
| case DATEOID: | ||
| case TIMEOID: | ||
| { | ||
| return DBType_String; | ||
| } | ||
|
|
||
| case POINTOID: | ||
| case LSEGOID: | ||
| case PATHOID: | ||
| case BOXOID: | ||
| case POLYGONOID: | ||
| { | ||
| return DBType_Blob; | ||
| } | ||
| default: | ||
| { | ||
| return DBType_String; | ||
| } | ||
| } | ||
|
|
||
| return DBType_Unknown; | ||
| } | ||
|
|
||
| PgDatabase::PgDatabase(PGconn *pgsql, const DatabaseInfo *info, bool persistent) | ||
| : m_pgsql(pgsql), m_lastInsertID(0), m_lastAffectedRows(0), m_preparedStatementID(0), | ||
| 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; | ||
|
|
||
| // DBI, for historical reasons, guarantees an initial refcount of 1. | ||
| AddRef(); | ||
| } | ||
|
|
||
| PgDatabase::~PgDatabase() | ||
| { | ||
| if (m_bPersistent) | ||
| g_PgDriver.RemoveFromList(this, true); | ||
| PQfinish(m_pgsql); | ||
|
|
||
| // libpg doesn't keep track of open resultsets of a connection. | ||
| // maybe we should track all opened results and clear them here? | ||
| // Otherwise there might be a memory leak, if the plugin doesn't close all result handles properly. | ||
| // There is no restriction on the order of closing the database connection and the result handles though. | ||
| } | ||
|
|
||
| void PgDatabase::IncReferenceCount() | ||
| { | ||
| AddRef(); | ||
| } | ||
|
|
||
| void PgDatabase::SetLastIDAndRows(unsigned int insertID, unsigned int affectedRows) | ||
| { | ||
| std::lock_guard<std::mutex> lock(m_LastQueryInfoLock); | ||
| // Also remember the last query's insert id and affected rows. postgresql only stores them per query. | ||
| m_lastInsertID = insertID; | ||
| m_lastAffectedRows = affectedRows; | ||
| } | ||
|
|
||
| bool PgDatabase::Close() | ||
| { | ||
| return !Release(); | ||
| } | ||
|
|
||
| const DatabaseInfo &PgDatabase::GetInfo() | ||
| { | ||
| return m_Info; | ||
| } | ||
|
|
||
| unsigned int PgDatabase::GetInsertID() | ||
| { | ||
| std::lock_guard<std::mutex> lock(m_LastQueryInfoLock); | ||
| return m_lastInsertID; | ||
| } | ||
|
|
||
| unsigned int PgDatabase::GetAffectedRows() | ||
| { | ||
| std::lock_guard<std::mutex> lock(m_LastQueryInfoLock); | ||
| return m_lastAffectedRows; | ||
| } | ||
|
|
||
| const char *PgDatabase::GetError(int *errCode) | ||
| { | ||
| if (errCode) | ||
| { | ||
| // PostgreSQL only supports SQLSTATE error codes. | ||
| // https://www.postgresql.org/docs/9.6/errcodes-appendix.html | ||
| *errCode = -1; | ||
| } | ||
|
|
||
| return PQerrorMessage(m_pgsql); | ||
| } | ||
|
|
||
| bool PgDatabase::QuoteString(const char *str, char buffer[], size_t maxlength, size_t *newSize) | ||
| { | ||
| size_t size = strlen(str); | ||
| size_t needed = size * 2 + 1; | ||
|
|
||
| if (maxlength < needed) | ||
| { | ||
| if (newSize) | ||
| { | ||
| *newSize = (size_t)needed; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| int error = 0; | ||
| needed = PQescapeStringConn(m_pgsql, buffer, str, size, &error); | ||
| if (newSize) | ||
| { | ||
| *newSize = (size_t)needed; | ||
| } | ||
|
|
||
| return error == 0; | ||
| } | ||
|
|
||
| bool PgDatabase::DoSimpleQuery(const char *query) | ||
| { | ||
| IQuery *pQuery = DoQuery(query); | ||
| if (!pQuery) | ||
| { | ||
| return false; | ||
| } | ||
| pQuery->Destroy(); | ||
| return true; | ||
| } | ||
|
|
||
| IQuery *PgDatabase::DoQuery(const char *query) | ||
| { | ||
| PGresult *res = PQexec(m_pgsql, query); | ||
|
|
||
| ExecStatusType status = PQresultStatus(res); | ||
|
|
||
| if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) | ||
| { | ||
| PQclear(res); | ||
| return NULL; | ||
| } | ||
|
|
||
| return new PgQuery(this, res); | ||
| } | ||
|
|
||
| bool PgDatabase::DoSimpleQueryEx(const char *query, size_t len) | ||
| { | ||
| IQuery *pQuery = DoQueryEx(query, len); | ||
| if (!pQuery) | ||
| { | ||
| return false; | ||
| } | ||
| pQuery->Destroy(); | ||
| return true; | ||
| } | ||
|
|
||
| IQuery *PgDatabase::DoQueryEx(const char *query, size_t len) | ||
| { | ||
| // There is no way to send binary data like that in queries. | ||
| // You'd need to escape the value with PQescapeByteaConn first and use it in the query string as usual. | ||
| return DoQuery(query); | ||
| } | ||
|
|
||
| unsigned int PgDatabase::GetAffectedRowsForQuery(IQuery *query) | ||
| { | ||
| return static_cast<PgQuery*>(query)->GetAffectedRows(); | ||
| } | ||
|
|
||
| unsigned int PgDatabase::GetInsertIDForQuery(IQuery *query) | ||
| { | ||
| return static_cast<PgQuery*>(query)->GetInsertID(); | ||
| } | ||
|
|
||
| IPreparedQuery *PgDatabase::PrepareQuery(const char *query, char *error, size_t maxlength, int *errCode) | ||
| { | ||
| char stmtName[10]; | ||
| // Maybe needs some locking around m_preparedStatementID++? | ||
| unsigned int stmtID = m_preparedStatementID; | ||
| snprintf(stmtName, 10, "%d", stmtID); | ||
| m_preparedStatementID++; | ||
|
|
||
| // Let postgresql guess the types of the arguments if there are any.. | ||
| PGresult *res = PQprepare(m_pgsql, stmtName, query, 0, NULL); | ||
|
|
||
| if (PQresultStatus(res) != PGRES_COMMAND_OK) | ||
| { | ||
| if (error) | ||
| { | ||
| strncopy(error, PQresultErrorMessage(res), maxlength); | ||
| } | ||
|
|
||
| if (errCode) | ||
| { | ||
| // PostgreSQL only supports SQLSTATE error codes. | ||
| // https://www.postgresql.org/docs/9.6/errcodes-appendix.html | ||
| *errCode = -1; | ||
| } | ||
|
|
||
| PQclear(res); | ||
| return NULL; | ||
| } | ||
|
|
||
| // Only the statement name is of importance. free the PGresult here. | ||
| PQclear(res); | ||
| return new PgStatement(this, stmtName); | ||
| } | ||
|
|
||
| bool PgDatabase::LockForFullAtomicOperation() | ||
| { | ||
| m_FullLock.lock(); | ||
| return true; | ||
| } | ||
|
|
||
| void PgDatabase::UnlockFromFullAtomicOperation() | ||
| { | ||
| m_FullLock.unlock(); | ||
| } | ||
|
|
||
| IDBDriver *PgDatabase::GetDriver() | ||
| { | ||
| return &g_PgDriver; | ||
| } | ||
|
|
||
| bool PgDatabase::SetCharacterSet(const char *characterset) | ||
| { | ||
| bool res; | ||
| LockForFullAtomicOperation(); | ||
| res = PQsetClientEncoding(m_pgsql, characterset) == 0; | ||
| UnlockFromFullAtomicOperation(); | ||
| return res; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| /** | ||
| * vim: set ts=4 : | ||
| * ============================================================================= | ||
| * SourceMod PostgreSQL Extension | ||
| * Copyright (C) 2013 AlliedModders LLC. All rights reserved. | ||
| * ============================================================================= | ||
| * | ||
| * This program is free software; you can redistribute it and/or modify it under | ||
| * the terms of the GNU General Public License, version 3.0, as published by the | ||
| * Free Software Foundation. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
| * details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License along with | ||
| * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| * | ||
| * As a special exception, AlliedModders LLC gives you permission to link the | ||
| * code of this program (as well as its derivative works) to "Half-Life 2," the | ||
| * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software | ||
| * by the Valve Corporation. You must obey the GNU General Public License in | ||
| * all respects for all other code used. Additionally, AlliedModders LLC grants | ||
| * this exception to all derivative works. AlliedModders LLC defines further | ||
| * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), | ||
| * or <http://www.sourcemod.net/license.php>. | ||
| * | ||
| * Version: $Id$ | ||
| */ | ||
|
|
||
| #ifndef _INCLUDE_SM_PGSQL_DATABASE_H_ | ||
| #define _INCLUDE_SM_PGSQL_DATABASE_H_ | ||
|
|
||
| #include <amtl/am-refcounting-threadsafe.h> | ||
| #include <mutex> | ||
| #include "PgDriver.h" | ||
|
|
||
| class PgQuery; | ||
| class PgStatement; | ||
|
|
||
| class PgDatabase | ||
| : public IDatabase, | ||
| public ke::RefcountedThreadsafe<PgDatabase> | ||
| { | ||
| friend class PgQuery; | ||
| friend class PgStatement; | ||
| public: | ||
| PgDatabase(PGconn *pgsql, const DatabaseInfo *info, bool persistent); | ||
| ~PgDatabase(); | ||
| public: //IDatabase | ||
| bool Close(); | ||
| 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(); | ||
| bool LockForFullAtomicOperation(); | ||
| void UnlockFromFullAtomicOperation(); | ||
| void IncReferenceCount(); | ||
| IDBDriver *GetDriver(); | ||
| bool DoSimpleQueryEx(const char *query, size_t len); | ||
| IQuery *DoQueryEx(const char *query, size_t len); | ||
| unsigned int GetAffectedRowsForQuery(IQuery *query); | ||
| unsigned int GetInsertIDForQuery(IQuery *query); | ||
| bool SetCharacterSet(const char *characterset); | ||
| public: | ||
| const DatabaseInfo &GetInfo(); | ||
| void SetLastIDAndRows(unsigned int insertID, unsigned int affectedRows); | ||
| private: | ||
| PGconn *m_pgsql; | ||
| std::mutex m_FullLock; | ||
|
|
||
| unsigned int m_lastInsertID; | ||
| unsigned int m_lastAffectedRows; | ||
| std::mutex m_LastQueryInfoLock; | ||
|
|
||
| unsigned int m_preparedStatementID; | ||
|
|
||
| /* ---------- */ | ||
| DatabaseInfo m_Info; | ||
| String m_Host; | ||
| String m_Database; | ||
| String m_User; | ||
| String m_Pass; | ||
| bool m_bPersistent; | ||
| }; | ||
|
|
||
| DBType GetOurType(Oid type); | ||
|
|
||
| #endif //_INCLUDE_SM_PGSQL_DATABASE_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,238 @@ | ||
| /** | ||
| * vim: set ts=4 : | ||
| * ============================================================================= | ||
| * SourceMod PostgreSQL Extension | ||
| * Copyright (C) 2013 AlliedModders LLC. All rights reserved. | ||
| * ============================================================================= | ||
| * | ||
| * This program is free software; you can redistribute it and/or modify it under | ||
| * the terms of the GNU General Public License, version 3.0, as published by the | ||
| * Free Software Foundation. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
| * details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License along with | ||
| * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| * | ||
| * As a special exception, AlliedModders LLC gives you permission to link the | ||
| * code of this program (as well as its derivative works) to "Half-Life 2," the | ||
| * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software | ||
| * by the Valve Corporation. You must obey the GNU General Public License in | ||
| * all respects for all other code used. Additionally, AlliedModders LLC grants | ||
| * this exception to all derivative works. AlliedModders LLC defines further | ||
| * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), | ||
| * or <http://www.sourcemod.net/license.php>. | ||
| * | ||
| * Version: $Id$ | ||
| */ | ||
|
|
||
| #include "PgDriver.h" | ||
| #include "PgDatabase.h" | ||
| #include "smsdk_ext.h" | ||
|
|
||
| PgDriver g_PgDriver; | ||
|
|
||
| PgDriver::PgDriver() | ||
| { | ||
| m_Handle = BAD_HANDLE; | ||
| } | ||
|
|
||
| void CloseDBList(List<PgDatabase *> &l) | ||
| { | ||
| List<PgDatabase *>::iterator iter; | ||
| for (iter=l.begin(); iter!=l.end(); iter++) | ||
| { | ||
| PgDatabase *db = (*iter); | ||
| while (!db->Close()) | ||
| { | ||
| /* Spool until it closes */ | ||
| } | ||
| } | ||
| l.clear(); | ||
| } | ||
|
|
||
| void PgDriver::Shutdown() | ||
| { | ||
| List<PgDatabase *>::iterator iter; | ||
| CloseDBList(m_PermDbs); | ||
|
|
||
| if (m_Handle != BAD_HANDLE) | ||
| { | ||
| dbi->ReleaseHandle(m_Handle, DBHandle_Driver, myself->GetIdentity()); | ||
| m_Handle = BAD_HANDLE; | ||
| } | ||
| } | ||
|
|
||
| const char *PgDriver::GetIdentifier() | ||
| { | ||
| return "pgsql"; | ||
| } | ||
|
|
||
| Handle_t PgDriver::GetHandle() | ||
| { | ||
| if (m_Handle == BAD_HANDLE) | ||
| { | ||
| m_Handle = dbi->CreateHandle(DBHandle_Driver, this, myself->GetIdentity()); | ||
| } | ||
|
|
||
| return m_Handle; | ||
| } | ||
|
|
||
| IdentityToken_t *PgDriver::GetIdentity() | ||
| { | ||
| return myself->GetIdentity(); | ||
| } | ||
|
|
||
| const char *PgDriver::GetProductName() | ||
| { | ||
| return "PostgreSQL"; | ||
| } | ||
|
|
||
| PGconn *Connect(const DatabaseInfo *info, char *error, size_t maxlength) | ||
| { | ||
| /* https://www.postgresql.org/docs/9.6/libpq-connect.html#LIBPQ-CONNSTRING */ | ||
| /* TODO: Switch to PQconnectdbParams to prevent escaping issues. */ | ||
| char *options = new char[1024]; | ||
peace-maker marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| int offs = snprintf(options, 1024, "host='%s' dbname='%s'", info->host, info->database); | ||
|
|
||
| if (info->port > 0) | ||
| { | ||
| offs += snprintf(&options[offs], 1024 - offs, " port=%d", info->port); | ||
| } | ||
|
|
||
| if (info->user[0] != '\0') | ||
| { | ||
| offs += snprintf(&options[offs], 1024 - offs, " user='%s'", info->user); | ||
| } | ||
| if (info->pass[0] != '\0') | ||
| { | ||
| offs += snprintf(&options[offs], 1024 - offs, " password='%s'", info->pass); | ||
| } | ||
|
|
||
| if (info->maxTimeout > 0) | ||
| { | ||
| offs += snprintf(&options[offs], 1024 - offs, " connect_timeout=%d", info->maxTimeout); | ||
| } | ||
|
|
||
| /* Make a connection to the database */ | ||
| PGconn *conn = PQconnectdb(options); | ||
|
|
||
| delete [] options; | ||
|
|
||
| /* Check to see that the backend connection was successfully made */ | ||
| if (PQstatus(conn) != CONNECTION_OK) | ||
| { | ||
| /* :TODO: expose UTIL_Format from smutil! */ | ||
| snprintf(error, maxlength, "%s", PQerrorMessage(conn)); | ||
| PQfinish(conn); | ||
| return NULL; | ||
| } | ||
|
|
||
| // :TODO: Check for connection problems everytime we talk to the database server and call PQreset on it, when it fails. | ||
| // There is no automatic reconnect in libpg. | ||
| // While we're at that. Save the prepared queries and rerun them when we reconnect! | ||
|
|
||
| return conn; | ||
| } | ||
|
|
||
| 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) == 0); | ||
| } | ||
|
|
||
| IDatabase *PgDriver::Connect(const DatabaseInfo *info, bool persistent, char *error, size_t maxlength) | ||
| { | ||
| std::lock_guard<std::mutex> lock(m_Lock); | ||
|
|
||
| if (persistent) | ||
| { | ||
| /* Try to find a matching persistent connection */ | ||
| List<PgDatabase *>::iterator iter; | ||
| for (iter=m_PermDbs.begin(); | ||
| iter!=m_PermDbs.end(); | ||
| iter++) | ||
| { | ||
| PgDatabase *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->IncReferenceCount(); | ||
| return db; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| PGconn *pgsql = ::Connect(info, error, maxlength); | ||
| if (!pgsql) | ||
| { | ||
| return NULL; | ||
| } | ||
|
|
||
| PgDatabase *db = new PgDatabase(pgsql, info, persistent); | ||
|
|
||
| if (persistent) | ||
| { | ||
| m_PermDbs.push_back(db); | ||
| } | ||
|
|
||
| return db; | ||
| } | ||
|
|
||
| void PgDriver::RemoveFromList(PgDatabase *pdb, bool persistent) | ||
| { | ||
| std::lock_guard<std::mutex> lock(m_Lock); | ||
| if (persistent) | ||
| { | ||
| m_PermDbs.remove(pdb); | ||
| } | ||
| } | ||
|
|
||
| bool PgDriver::IsThreadSafe() | ||
| { | ||
| return (PQisthreadsafe() != 0); | ||
| } | ||
|
|
||
| bool PgDriver::InitializeThreadSafety() | ||
| { | ||
| return IsThreadSafe(); | ||
| } | ||
|
|
||
| void PgDriver::ShutdownThreadSafety() | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| 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); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| /** | ||
| * vim: set ts=4 : | ||
| * ============================================================================= | ||
| * SourceMod PostgreSQL Extension | ||
| * Copyright (C) 2013 AlliedModders LLC. All rights reserved. | ||
| * ============================================================================= | ||
| * | ||
| * This program is free software; you can redistribute it and/or modify it under | ||
| * the terms of the GNU General Public License, version 3.0, as published by the | ||
| * Free Software Foundation. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
| * details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License along with | ||
| * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| * | ||
| * As a special exception, AlliedModders LLC gives you permission to link the | ||
| * code of this program (as well as its derivative works) to "Half-Life 2," the | ||
| * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software | ||
| * by the Valve Corporation. You must obey the GNU General Public License in | ||
| * all respects for all other code used. Additionally, AlliedModders LLC grants | ||
| * this exception to all derivative works. AlliedModders LLC defines further | ||
| * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), | ||
| * or <http://www.sourcemod.net/license.php>. | ||
| * | ||
| * Version: $Id$ | ||
| */ | ||
|
|
||
| #ifndef _INCLUDE_SM_PGSQL_DRIVER_H_ | ||
| #define _INCLUDE_SM_PGSQL_DRIVER_H_ | ||
|
|
||
| #define SOURCEMOD_SQL_DRIVER_CODE | ||
| #include <IDBDriver.h> | ||
| #include <sm_platform.h> | ||
| #if defined PLATFORM_WINDOWS | ||
| #include <winsock.h> | ||
| #endif | ||
|
|
||
| #include <libpq-fe.h> | ||
|
|
||
| #include <sh_string.h> | ||
| #include <sh_list.h> | ||
|
|
||
| #include <mutex> | ||
|
|
||
| using namespace SourceMod; | ||
| using namespace SourceHook; | ||
|
|
||
| #define M_CLIENT_MULTI_RESULTS ((1) << 17) /* Enable/disable multi-results */ | ||
|
|
||
| class PgDatabase; | ||
|
|
||
| class PgDriver : public IDBDriver | ||
| { | ||
| public: | ||
| PgDriver(); | ||
| 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(); | ||
| bool IsThreadSafe(); | ||
| bool InitializeThreadSafety(); | ||
| void ShutdownThreadSafety(); | ||
| public: | ||
| void Shutdown(); | ||
| void RemoveFromList(PgDatabase *pdb, bool persistent); | ||
| private: | ||
| std::mutex m_Lock; | ||
| Handle_t m_Handle; | ||
| List<PgDatabase *> m_PermDbs; | ||
| }; | ||
|
|
||
| extern PgDriver g_PgDriver; | ||
|
|
||
| unsigned int strncopy(char *dest, const char *src, size_t count); | ||
|
|
||
| #endif //_INCLUDE_SM_PGSQL_DRIVER_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,350 @@ | ||
| /** | ||
| * vim: set ts=4 : | ||
| * ============================================================================= | ||
| * SourceMod PostgreSQL Extension | ||
| * Copyright (C) 2013 AlliedModders LLC. All rights reserved. | ||
| * ============================================================================= | ||
| * | ||
| * This program is free software; you can redistribute it and/or modify it under | ||
| * the terms of the GNU General Public License, version 3.0, as published by the | ||
| * Free Software Foundation. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
| * details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License along with | ||
| * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| * | ||
| * As a special exception, AlliedModders LLC gives you permission to link the | ||
| * code of this program (as well as its derivative works) to "Half-Life 2," the | ||
| * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software | ||
| * by the Valve Corporation. You must obey the GNU General Public License in | ||
| * all respects for all other code used. Additionally, AlliedModders LLC grants | ||
| * this exception to all derivative works. AlliedModders LLC defines further | ||
| * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), | ||
| * or <http://www.sourcemod.net/license.php>. | ||
| * | ||
| * Version: $Id$ | ||
| */ | ||
|
|
||
| #include "PgStatement.h" | ||
|
|
||
| PgStatement::PgStatement(PgDatabase *db, const char* stmtName) | ||
| : m_pgsql(db->m_pgsql), m_pParent(db), m_insertID(0), m_affectedRows(0), m_rs(NULL), m_Results(false) | ||
| { | ||
| m_stmtName = new char[10]; | ||
| strncopy(m_stmtName, stmtName, 10); | ||
|
|
||
| PGresult *desc = PQdescribePrepared(m_pgsql, m_stmtName); | ||
|
|
||
| // TODO: Proper error handling? | ||
| if (PQresultStatus(desc) != PGRES_COMMAND_OK) | ||
| { | ||
| PQclear(desc); | ||
| return; | ||
| } | ||
|
|
||
| m_Params = (unsigned int)PQnparams(desc); | ||
|
|
||
| if (m_Params) | ||
| { | ||
| m_pushinfo = (ParamBind *)malloc(sizeof(ParamBind) * m_Params); | ||
| memset(m_pushinfo, 0, sizeof(ParamBind) * m_Params); | ||
| } else { | ||
| m_pushinfo = NULL; | ||
| } | ||
|
|
||
| m_Results = false; | ||
| } | ||
|
|
||
| PgStatement::~PgStatement() | ||
| { | ||
| /* Free result set structures */ | ||
| if (m_Results) | ||
| { | ||
| if (m_rs->m_pRes != NULL) | ||
| PQclear(m_rs->m_pRes); | ||
| 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); | ||
| } | ||
|
|
||
| void PgStatement::Destroy() | ||
| { | ||
| delete this; | ||
| } | ||
|
|
||
| bool PgStatement::FetchMoreResults() | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| void *PgStatement::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 PgStatement::BindParamInt(unsigned int param, int num, bool signd) | ||
| { | ||
| if (param >= m_Params) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| m_pushinfo[param].data.ival = num; | ||
| m_pushinfo[param].type = DBType_Integer; | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool PgStatement::BindParamFloat(unsigned int param, float f) | ||
| { | ||
| if (param >= m_Params) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| m_pushinfo[param].data.fval = f; | ||
| m_pushinfo[param].type = DBType_Float; | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool PgStatement::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_pushinfo[param].blob = (void *)final_ptr; | ||
| m_pushinfo[param].length = len; | ||
| m_pushinfo[param].type = DBType_String; | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool PgStatement::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_pushinfo[param].blob = (void *)final_ptr; | ||
| m_pushinfo[param].length = length; | ||
| m_pushinfo[param].type = DBType_Blob; | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool PgStatement::BindParamNull(unsigned int param) | ||
| { | ||
| if (param >= m_Params) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| m_pushinfo[param].type = DBType_NULL; | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool PgStatement::Execute() | ||
| { | ||
| /* Clear any past result first! */ | ||
| if (m_Results) | ||
| delete m_rs; | ||
| m_Results = false; | ||
|
|
||
| PGresult *res; | ||
| /* Bind the parameters */ | ||
| if (m_Params) | ||
| { | ||
| // Put bound params into a nice array | ||
| const char **paramValues = new const char*[m_Params]; | ||
| int *paramLengths = new int[m_Params]; | ||
| int *paramFormats = new int[m_Params]; | ||
|
|
||
| for (unsigned int i=0; i<m_Params; i++) | ||
| { | ||
| switch (m_pushinfo[i].type) | ||
| { | ||
| case DBType_Integer: | ||
| { | ||
| char *buf = new char[64]; // 64 chars should be enough? | ||
| snprintf(buf, 64, "%d", m_pushinfo[i].data.ival); | ||
| paramValues[i] = buf; | ||
| paramLengths[i] = 0; | ||
| paramFormats[i] = 0; | ||
| break; | ||
| } | ||
| case DBType_Float: | ||
| { | ||
| char *buf = new char[64]; // 64 chars should be enough? | ||
| snprintf(buf, 64, "%f", m_pushinfo[i].data.fval); | ||
| paramValues[i] = buf; | ||
| paramLengths[i] = 0; | ||
| paramFormats[i] = 0; | ||
| break; | ||
| } | ||
| case DBType_String: | ||
| { | ||
| paramValues[i] = (char *)m_pushinfo[i].blob; | ||
| paramLengths[i] = m_pushinfo[i].length; | ||
| paramFormats[i] = 0; | ||
| break; | ||
| } | ||
| case DBType_Blob: | ||
| { | ||
| paramValues[i] = (char *)m_pushinfo[i].blob; | ||
| paramLengths[i] = m_pushinfo[i].length; | ||
| paramFormats[i] = 1; | ||
| break; | ||
| } | ||
| case DBType_NULL: | ||
| default: | ||
| { | ||
| paramValues[i] = NULL; | ||
| paramLengths[i] = 0; | ||
| paramFormats[i] = 0; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| res = PQexecPrepared(m_pgsql, m_stmtName, m_Params, paramValues, paramLengths, paramFormats, 0); | ||
| delete [] paramFormats; | ||
| delete [] paramLengths; | ||
|
|
||
| // .. need to free our char buffers | ||
| for (unsigned int i=0; i<m_Params; i++) | ||
| { | ||
| switch (m_pushinfo[i].type) | ||
| { | ||
| case DBType_Integer: | ||
| case DBType_Float: | ||
| { | ||
| delete paramValues[i]; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| delete [] paramValues; | ||
| } | ||
| // There are no parameters to be bound! | ||
| else | ||
| { | ||
| res = PQexecPrepared(m_pgsql, m_stmtName, 0, NULL, NULL, NULL, 0); | ||
| } | ||
|
|
||
| ExecStatusType status = PQresultStatus(res); | ||
| if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) | ||
| { | ||
| PQclear(res); | ||
| return false; | ||
| } | ||
|
|
||
| m_affectedRows = (unsigned int)atoi(PQcmdTuples(res)); | ||
| m_insertID = (unsigned int)PQoidValue(res); | ||
|
|
||
| m_pParent->SetLastIDAndRows(m_insertID, m_affectedRows); | ||
|
|
||
| /* Skip away if we don't have data */ | ||
| if (status == PGRES_COMMAND_OK) | ||
| { | ||
| PQclear(res); | ||
| return true; | ||
| } | ||
|
|
||
| m_rs = new PgBasicResults(res); | ||
| m_Results = true; | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| const char *PgStatement::GetError(int *errCode/* =NULL */) | ||
| { | ||
| if (m_Results) | ||
| { | ||
| if (errCode) | ||
| { | ||
| // PostgreSQL only supports SQLSTATE error codes. | ||
| // https://www.postgresql.org/docs/9.6/errcodes-appendix.html | ||
| *errCode = -1; | ||
| } | ||
|
|
||
| return PQresultErrorMessage(m_rs->m_pRes); | ||
| } | ||
| else | ||
| { | ||
| return PQerrorMessage(m_pgsql); | ||
| } | ||
| } | ||
|
|
||
| unsigned int PgStatement::GetAffectedRows() | ||
| { | ||
| return m_affectedRows; | ||
| } | ||
|
|
||
| unsigned int PgStatement::GetInsertID() | ||
| { | ||
| return m_insertID; | ||
| } | ||
|
|
||
| IResultSet *PgStatement::GetResultSet() | ||
| { | ||
| return (m_Results ? m_rs : NULL); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| /** | ||
| * vim: set ts=4 : | ||
| * ============================================================================= | ||
| * SourceMod PostgreSQL Extension | ||
| * Copyright (C) 2013 AlliedModders LLC. All rights reserved. | ||
| * ============================================================================= | ||
| * | ||
| * This program is free software; you can redistribute it and/or modify it under | ||
| * the terms of the GNU General Public License, version 3.0, as published by the | ||
| * Free Software Foundation. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
| * details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License along with | ||
| * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| * | ||
| * As a special exception, AlliedModders LLC gives you permission to link the | ||
| * code of this program (as well as its derivative works) to "Half-Life 2," the | ||
| * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software | ||
| * by the Valve Corporation. You must obey the GNU General Public License in | ||
| * all respects for all other code used. Additionally, AlliedModders LLC grants | ||
| * this exception to all derivative works. AlliedModders LLC defines further | ||
| * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), | ||
| * or <http://www.sourcemod.net/license.php>. | ||
| * | ||
| * Version: $Id$ | ||
| */ | ||
|
|
||
| #ifndef _INCLUDE_SM_PGSQL_STATEMENT_H_ | ||
| #define _INCLUDE_SM_PGSQL_STATEMENT_H_ | ||
|
|
||
| #include "PgDatabase.h" | ||
| #include "PgBasicResults.h" | ||
|
|
||
| struct ParamBind | ||
| { | ||
| union | ||
| { | ||
| float fval; | ||
| int ival; | ||
| } data; | ||
| void *blob; | ||
| size_t length; | ||
| DBType type; | ||
| }; | ||
|
|
||
| class PgStatement : public IPreparedQuery | ||
| { | ||
| public: | ||
| PgStatement(PgDatabase *db, const char* stmtName); | ||
| ~PgStatement(); | ||
| 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: | ||
| PGconn *m_pgsql; | ||
| ke::RefPtr<PgDatabase> m_pParent; | ||
| char *m_stmtName; | ||
|
|
||
| ParamBind *m_pushinfo; | ||
| unsigned int m_Params; | ||
|
|
||
| unsigned int m_insertID; | ||
| unsigned int m_affectedRows; | ||
| PgBasicResults *m_rs; | ||
| bool m_Results; | ||
| }; | ||
|
|
||
| #endif //_INCLUDE_SM_PGSQL_STATEMENT_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| /*------------------------------------------------------------------------- | ||
| * | ||
| * postgres_ext.h | ||
| * | ||
| * This file contains declarations of things that are visible everywhere | ||
| * in PostgreSQL *and* are visible to clients of frontend interface libraries. | ||
| * For example, the Oid type is part of the API of libpq and other libraries. | ||
| * | ||
| * Declarations which are specific to a particular interface should | ||
| * go in the header file for that interface (such as libpq-fe.h). This | ||
| * file is only for fundamental Postgres declarations. | ||
| * | ||
| * User-written C functions don't count as "external to Postgres." | ||
| * Those function much as local modifications to the backend itself, and | ||
| * use header files that are otherwise internal to Postgres to interface | ||
| * with the backend. | ||
| * | ||
| * src/include/postgres_ext.h | ||
| * | ||
| *------------------------------------------------------------------------- | ||
| */ | ||
|
|
||
| #ifndef POSTGRES_EXT_H | ||
| #define POSTGRES_EXT_H | ||
|
|
||
| #include "pg_config_ext.h" | ||
|
|
||
| /* | ||
| * Object ID is a fundamental type in Postgres. | ||
| */ | ||
| typedef unsigned int Oid; | ||
|
|
||
| #ifdef __cplusplus | ||
| #define InvalidOid (Oid(0)) | ||
| #else | ||
| #define InvalidOid ((Oid) 0) | ||
| #endif | ||
|
|
||
| #define OID_MAX UINT_MAX | ||
| /* you will need to include <limits.h> to use the above #define */ | ||
|
|
||
| /* Define a signed 64-bit integer type for use in client API declarations. */ | ||
| typedef PG_INT64_TYPE pg_int64; | ||
|
|
||
|
|
||
| /* | ||
| * Identifiers of error message fields. Kept here to keep common | ||
| * between frontend and backend, and also to export them to libpq | ||
| * applications. | ||
| */ | ||
| #define PG_DIAG_SEVERITY 'S' | ||
| #define PG_DIAG_SEVERITY_NONLOCALIZED 'V' | ||
| #define PG_DIAG_SQLSTATE 'C' | ||
| #define PG_DIAG_MESSAGE_PRIMARY 'M' | ||
| #define PG_DIAG_MESSAGE_DETAIL 'D' | ||
| #define PG_DIAG_MESSAGE_HINT 'H' | ||
| #define PG_DIAG_STATEMENT_POSITION 'P' | ||
| #define PG_DIAG_INTERNAL_POSITION 'p' | ||
| #define PG_DIAG_INTERNAL_QUERY 'q' | ||
| #define PG_DIAG_CONTEXT 'W' | ||
| #define PG_DIAG_SCHEMA_NAME 's' | ||
| #define PG_DIAG_TABLE_NAME 't' | ||
| #define PG_DIAG_COLUMN_NAME 'c' | ||
| #define PG_DIAG_DATATYPE_NAME 'd' | ||
| #define PG_DIAG_CONSTRAINT_NAME 'n' | ||
| #define PG_DIAG_SOURCE_FILE 'F' | ||
| #define PG_DIAG_SOURCE_LINE 'L' | ||
| #define PG_DIAG_SOURCE_FUNCTION 'R' | ||
|
|
||
| #endif /* POSTGRES_EXT_H */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| /** | ||
| * vim: set ts=4 : | ||
| * ============================================================================= | ||
| * SourceMod PostgreSQL Extension | ||
| * Copyright (C) 2013 AlliedModders LLC. All rights reserved. | ||
| * ============================================================================= | ||
| * | ||
| * This program is free software; you can redistribute it and/or modify it under | ||
| * the terms of the GNU General Public License, version 3.0, as published by the | ||
| * Free Software Foundation. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
| * details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License along with | ||
| * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| * | ||
| * As a special exception, AlliedModders LLC gives you permission to link the | ||
| * code of this program (as well as its derivative works) to "Half-Life 2," the | ||
| * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software | ||
| * by the Valve Corporation. You must obey the GNU General Public License in | ||
| * all respects for all other code used. Additionally, AlliedModders LLC grants | ||
| * this exception to all derivative works. AlliedModders LLC defines further | ||
| * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), | ||
| * or <http://www.sourcemod.net/license.php>. | ||
| * | ||
| * Version: $Id$ | ||
| */ | ||
|
|
||
| #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 "PostgreSQL-DBI" | ||
| #define SMEXT_CONF_DESCRIPTION "PostgreSQL driver implementation for DBI" | ||
| #define SMEXT_CONF_VERSION "" | ||
| #define SMEXT_CONF_AUTHOR "AlliedModders LLC" | ||
| #define SMEXT_CONF_URL "http://www.sourcemod.net/" | ||
| #define SMEXT_CONF_LOGTAG "PGSQL" | ||
| #define SMEXT_CONF_LICENSE "GPL" | ||
| #define SMEXT_CONF_DATESTRING "" | ||
|
|
||
| /** | ||
| * @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 | ||
| //#define SMEXT_ENABLE_GAMECONF | ||
| //#define SMEXT_ENABLE_MEMUTILS | ||
| //#define SMEXT_ENABLE_GAMEHELPERS | ||
| //#define SMEXT_ENABLE_TIMERSYS | ||
| //#define SMEXT_ENABLE_THREADER | ||
|
|
||
| #endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| // Microsoft Visual C++ generated resource script. | ||
| // | ||
| //#include "resource.h" | ||
|
|
||
| #define APSTUDIO_READONLY_SYMBOLS | ||
| ///////////////////////////////////////////////////////////////////////////// | ||
| // | ||
| // Generated from the TEXTINCLUDE 2 resource. | ||
| // | ||
| #include "winres.h" | ||
|
|
||
| #include <sourcemod_version.h> | ||
|
|
||
| ///////////////////////////////////////////////////////////////////////////// | ||
| #undef APSTUDIO_READONLY_SYMBOLS | ||
|
|
||
| ///////////////////////////////////////////////////////////////////////////// | ||
| // English (U.S.) resources | ||
|
|
||
| #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) | ||
| #ifdef _WIN32 | ||
| LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US | ||
| #pragma code_page(1252) | ||
| #endif //_WIN32 | ||
|
|
||
| ///////////////////////////////////////////////////////////////////////////// | ||
| // | ||
| // Version | ||
| // | ||
|
|
||
| VS_VERSION_INFO VERSIONINFO | ||
| FILEVERSION SM_VERSION_FILE | ||
| PRODUCTVERSION SM_VERSION_FILE | ||
| FILEFLAGSMASK 0x17L | ||
| #ifdef _DEBUG | ||
| FILEFLAGS 0x1L | ||
| #else | ||
| FILEFLAGS 0x0L | ||
| #endif | ||
| FILEOS 0x4L | ||
| FILETYPE 0x2L | ||
| FILESUBTYPE 0x0L | ||
| BEGIN | ||
| BLOCK "StringFileInfo" | ||
| BEGIN | ||
| BLOCK "000004b0" | ||
| BEGIN | ||
| VALUE "Comments", "PostgreSQL Extension" | ||
| VALUE "FileDescription", "SourceMod PostgreSQL Extension" | ||
| VALUE "FileVersion", SM_VERSION_STRING | ||
| VALUE "InternalName", "SourceMod PostgreSQL Extension" | ||
| VALUE "LegalCopyright", "Copyright (c) 2004-2013, AlliedModders LLC" | ||
| VALUE "OriginalFilename", BINARY_NAME | ||
| VALUE "ProductName", "SourceMod PostgreSQL Extension" | ||
| VALUE "ProductVersion", SM_VERSION_STRING | ||
| END | ||
| END | ||
| BLOCK "VarFileInfo" | ||
| BEGIN | ||
| VALUE "Translation", 0x0, 1200 | ||
| END | ||
| END | ||
|
|
||
|
|
||
| #ifdef APSTUDIO_INVOKED | ||
| ///////////////////////////////////////////////////////////////////////////// | ||
| // | ||
| // TEXTINCLUDE | ||
| // | ||
|
|
||
| 1 TEXTINCLUDE | ||
| BEGIN | ||
| "resource.h\0" | ||
| END | ||
|
|
||
| 2 TEXTINCLUDE | ||
| BEGIN | ||
| "#include ""winres.h""\r\n" | ||
| "\0" | ||
| END | ||
|
|
||
| 3 TEXTINCLUDE | ||
| BEGIN | ||
| "\r\n" | ||
| "\0" | ||
| END | ||
|
|
||
| #endif // APSTUDIO_INVOKED | ||
|
|
||
| #endif // English (U.S.) resources | ||
| ///////////////////////////////////////////////////////////////////////////// | ||
|
|
||
|
|
||
|
|
||
| #ifndef APSTUDIO_INVOKED | ||
| ///////////////////////////////////////////////////////////////////////////// | ||
| // | ||
| // Generated from the TEXTINCLUDE 3 resource. | ||
| // | ||
|
|
||
|
|
||
| ///////////////////////////////////////////////////////////////////////////// | ||
| #endif // not APSTUDIO_INVOKED | ||
|
|