diff --git a/engine/shared/library/CMakeLists.txt b/engine/shared/library/CMakeLists.txt index aff2f340..e327667e 100644 --- a/engine/shared/library/CMakeLists.txt +++ b/engine/shared/library/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(sharedCollision) add_subdirectory(sharedCommandParser) add_subdirectory(sharedCompression) +add_subdirectory(sharedDatabaseInterface) add_subdirectory(sharedDebug) add_subdirectory(sharedFile) add_subdirectory(sharedFoundation) diff --git a/engine/shared/library/sharedDatabaseInterface/CMakeLists.txt b/engine/shared/library/sharedDatabaseInterface/CMakeLists.txt new file mode 100644 index 00000000..c5470d2c --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 2.8) + +project(sharedDatabaseInterface) + +if(WIN32) + add_definitions(/D_CRT_SECURE_NO_WARNINGS) +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/public) + +add_subdirectory(src) +add_subdirectory(src_oci) diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/Bindable.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/Bindable.h new file mode 100644 index 00000000..d342bd3f --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/Bindable.h @@ -0,0 +1 @@ +#include "../../src/shared/core/Bindable.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/BindableImplementation.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/BindableImplementation.h new file mode 100644 index 00000000..cd89cdf7 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/BindableImplementation.h @@ -0,0 +1 @@ +#include "../../src/shared/core/BindableImplementation.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/BindableNetworkId.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/BindableNetworkId.h new file mode 100644 index 00000000..5c19c3af --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/BindableNetworkId.h @@ -0,0 +1 @@ +#include "../../src/shared/core/BindableNetworkId.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/BufferString.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/BufferString.h new file mode 100644 index 00000000..52b78c1a --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/BufferString.h @@ -0,0 +1 @@ +#include "../../src/shared/core/BufferString.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBatchQuery.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBatchQuery.h new file mode 100644 index 00000000..102e9ec4 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBatchQuery.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbBatchQuery.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableBase.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableBase.h new file mode 100644 index 00000000..f2b0648a --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableBase.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbBindableBase.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableBitArray.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableBitArray.h new file mode 100644 index 00000000..3e874e75 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableBitArray.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbBindableBitArray.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableBool.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableBool.h new file mode 100644 index 00000000..8f71e1f6 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableBool.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbBindableBool.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableDouble.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableDouble.h new file mode 100644 index 00000000..c100aab4 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableDouble.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbBindableDouble.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableInt64.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableInt64.h new file mode 100644 index 00000000..2ef3247a --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableInt64.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbBindableInt64.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableLong.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableLong.h new file mode 100644 index 00000000..b11e3a91 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableLong.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbBindableLong.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableString.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableString.h new file mode 100644 index 00000000..6fa3dc58 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableString.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbBindableString.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableUnicode.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableUnicode.h new file mode 100644 index 00000000..ed3635d6 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableUnicode.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbBindableUnicode.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableVarray.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableVarray.h new file mode 100644 index 00000000..5d743f13 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBindableVarray.h @@ -0,0 +1 @@ +#include "../../src_oci/DbBindableVarray.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBufferRow.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBufferRow.h new file mode 100644 index 00000000..7bd82d3f --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbBufferRow.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbBufferRow.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbException.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbException.h new file mode 100644 index 00000000..2f300e7f --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbException.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbException.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbLogEvent.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbLogEvent.h new file mode 100644 index 00000000..30b46948 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbLogEvent.h @@ -0,0 +1 @@ +#include "../../src/shared/logger/DbLogEvent.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbLogger.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbLogger.h new file mode 100644 index 00000000..eb7eff2f --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbLogger.h @@ -0,0 +1 @@ +#include "../../src/shared/logger/DbLogger.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbModeQuery.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbModeQuery.h new file mode 100644 index 00000000..028093d2 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbModeQuery.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbModeQuery.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbProfiler.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbProfiler.h new file mode 100644 index 00000000..02f0404a --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbProfiler.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbProfiler.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbProtocol.def b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbProtocol.def new file mode 100644 index 00000000..9e21414e --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbProtocol.def @@ -0,0 +1 @@ +#include "../../src/shared/core/DbProtocol.def" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbQuery.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbQuery.h new file mode 100644 index 00000000..fe609fa8 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbQuery.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbQuery.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbQueryImplementation.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbQueryImplementation.h new file mode 100644 index 00000000..3c33fba9 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbQueryImplementation.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbQueryImplementation.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbRow.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbRow.h new file mode 100644 index 00000000..78d261a9 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbRow.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbRow.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbServer.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbServer.h new file mode 100644 index 00000000..e89fa960 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbServer.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbServer.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbSession.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbSession.h new file mode 100644 index 00000000..6e5d9f2b --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbSession.h @@ -0,0 +1 @@ +#include "../../src/shared/core/DbSession.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbTaskQueue.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbTaskQueue.h new file mode 100644 index 00000000..5bc1149f --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbTaskQueue.h @@ -0,0 +1 @@ +#include "../../src/shared/queue/DbTaskQueue.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbTaskRequest.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbTaskRequest.h new file mode 100644 index 00000000..14e257e3 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbTaskRequest.h @@ -0,0 +1 @@ +#include "../../src/shared/queue/DbTaskRequest.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbTaskSaveLog.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbTaskSaveLog.h new file mode 100644 index 00000000..ee6de2c1 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbTaskSaveLog.h @@ -0,0 +1 @@ +#include "../../src/shared/logger/DbTaskSaveLog.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbTestQuery.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbTestQuery.h new file mode 100644 index 00000000..339ca1fb --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/DbTestQuery.h @@ -0,0 +1 @@ +#include "../../src/shared/queue/DbTestQuery.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/FirstSharedDatabaseInterface.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/FirstSharedDatabaseInterface.h new file mode 100644 index 00000000..12755354 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/FirstSharedDatabaseInterface.h @@ -0,0 +1 @@ +#include "../../src/shared/core/FirstSharedDatabaseInterface.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/NullEncodedStandardString.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/NullEncodedStandardString.h new file mode 100644 index 00000000..7772a059 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/NullEncodedStandardString.h @@ -0,0 +1 @@ +#include "../../src/shared/core/NullEncodedStandardString.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/NullEncodedUnicodeString.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/NullEncodedUnicodeString.h new file mode 100644 index 00000000..04f77f7b --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/NullEncodedUnicodeString.h @@ -0,0 +1 @@ +#include "../../src/shared/core/NullEncodedUnicodeString.h" diff --git a/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/SQLC_Defs.h b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/SQLC_Defs.h new file mode 100644 index 00000000..f0563199 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/include/public/sharedDatabaseInterface/SQLC_Defs.h @@ -0,0 +1,7 @@ +#if defined(PLATFORM_WIN32) +#include "../../src/win32/SQLC_Defs.h" +#elif defined(PLATFORM_LINUX) +#include "../../src/linux/SQLC_Defs.h" +#else +#error unsupported platform +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/CMakeLists.txt b/engine/shared/library/sharedDatabaseInterface/src/CMakeLists.txt new file mode 100644 index 00000000..7e0b3b91 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/CMakeLists.txt @@ -0,0 +1,94 @@ + +set(SHARED_SOURCES + shared/core/Bindable.cpp + shared/core/Bindable.h + shared/core/BindableImplementation.h + shared/core/BindableNetworkId.cpp + shared/core/BindableNetworkId.h + shared/core/BufferString.cpp + shared/core/BufferString.h + shared/core/DbBatchQuery.h + shared/core/DbBindableBase.cpp + shared/core/DbBindableBase.h + shared/core/DbBindableBitArray.cpp + shared/core/DbBindableBitArray.h + shared/core/DbBindableBool.cpp + shared/core/DbBindableBool.h + shared/core/DbBindableDouble.cpp + shared/core/DbBindableDouble.h + shared/core/DbBindableInt64.cpp + shared/core/DbBindableInt64.h + shared/core/DbBindableLong.cpp + shared/core/DbBindableLong.h + shared/core/DbBindableString.cpp + shared/core/DbBindableString.h + shared/core/DbBindableUnicode.cpp + shared/core/DbBindableUnicode.h + shared/core/DbBufferRow.cpp + shared/core/DbBufferRow.h + shared/core/DbException.cpp + shared/core/DbException.h + shared/core/DbModeQuery.cpp + shared/core/DbModeQuery.h + shared/core/DbProfiler.cpp + shared/core/DbProfiler.h + shared/core/DbProtocol.def + shared/core/DbQuery.cpp + shared/core/DbQuery.h + shared/core/DbQueryImplementation.h + shared/core/DbRow.cpp + shared/core/DbRow.h + shared/core/DbServer.cpp + shared/core/DbServer.h + shared/core/DbSession.cpp + shared/core/DbSession.h + shared/core/FirstSharedDatabaseInterface.h + shared/core/NullEncodedUnicodeString.cpp + shared/core/NullEncodedUnicodeString.h + shared/core/NullEncodedStandardString.cpp + shared/core/NullEncodedStandardString.h + + shared/logger/DbLogEvent.cpp + shared/logger/DbLogEvent.h + shared/logger/DbLogger.cpp + shared/logger/DbLogger.h + shared/logger/DbTaskSaveLog.cpp + shared/logger/DbTaskSaveLog.h + + shared/queue/DbTaskQueue.cpp + shared/queue/DbTaskQueue.h + shared/queue/DbTestQuery.cpp + shared/queue/DbTestQuery.h + shared/queue/DbTaskRequest.cpp + shared/queue/DbTaskRequest.h +) + +if(WIN32) + set(PLATFORM_SOURCES + win32/FirstSharedDatabaseInterface.cpp + win32/SQLC_Defs.h + ) +else() + set(PLATFORM_SOURCES + linux/SQLC_Defs.h + ) +endif() + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/shared + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedDebug/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundation/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundationTypes/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedLog/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMemoryManager/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedSynchronization/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedThread/include/public + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/archive/include + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/unicode/include + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/unicodeArchive/include/public +) + +add_library(sharedDatabaseInterface STATIC + ${SHARED_SOURCES} + ${PLATFORM_SOURCES} +) diff --git a/engine/shared/library/sharedDatabaseInterface/src/linux/SQLC_Defs.h b/engine/shared/library/sharedDatabaseInterface/src/linux/SQLC_Defs.h new file mode 100644 index 00000000..6443b953 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/linux/SQLC_Defs.h @@ -0,0 +1,18 @@ +/* SQLC_Defs.h + * + * This file includes common definitions needed by the other SQLClasses header files. + * (Don't include this file directly -- files that require it will include it.) + * + * Note that this file is specific to each OS, because what header files are + * needed varies in each OS. In particular, Unix versions don't want windows.h. + * + * ODBC versions: includes definitions of ODBC datatypes needed by the various + * SQLClasses + */ + +#ifndef _SQLC_DEFS_H +#define _SQLC_DEFS_H + +#include + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/Bindable.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/Bindable.cpp new file mode 100644 index 00000000..aa0f5d1d --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/Bindable.cpp @@ -0,0 +1,11 @@ +// ====================================================================== +// +// Bindable.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/Bindable.h" + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/Bindable.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/Bindable.h new file mode 100644 index 00000000..22617057 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/Bindable.h @@ -0,0 +1,30 @@ +/* Bindable.h + * + * Defines classes based on built-in simple types that know how to bind themselves to + * database queries and support null values. + * + * Each class supports the following functions: + * bool isNull -- test whether the value is null + * void setToNull -- set the value to null + * getValue -- return the value (make sure you test for null before calling this. The results + * are undefined if the Bindable is null.) + * setValue -- set the value + * + * ODBC version + * + * Including this header brings in all the bindable types. + */ + +#ifndef _BINDABLE_H +#define _BINDABLE_H + +#include "sharedDatabaseInterface/DbBindableBase.h" +#include "sharedDatabaseInterface/DbBindableBitArray.h" +#include "sharedDatabaseInterface/DbBindableBool.h" +#include "sharedDatabaseInterface/DbBindableDouble.h" +#include "sharedDatabaseInterface/DbBindableInt64.h" +#include "sharedDatabaseInterface/DbBindableLong.h" +#include "sharedDatabaseInterface/DbBindableString.h" +#include "sharedDatabaseInterface/DbBindableUnicode.h" + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/BindableImplementation.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/BindableImplementation.h new file mode 100644 index 00000000..402ee9d4 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/BindableImplementation.h @@ -0,0 +1,34 @@ +// ====================================================================== +// +// BindableImpl.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_BindableImpl_H +#define INCLUDED_BindableImpl_H + +// ====================================================================== + +namespace DB +{ + class Bindable; + + class BindableImpl + { + public: + BindableImpl(Bindable *owner); + virtual ~BindableImpl(); + + virtual bool isNull() const =0; + virtual void setNull() =0; + + protected: + Bindable *m_owner; + }; + +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/BindableNetworkId.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/BindableNetworkId.cpp new file mode 100644 index 00000000..25cdab4f --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/BindableNetworkId.cpp @@ -0,0 +1,32 @@ +// ====================================================================== +// +// BindableNetworkId.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/BindableNetworkId.h" + +using namespace DB; + +// ====================================================================== + +BindableNetworkId::BindableNetworkId() +{ +} + +// ---------------------------------------------------------------------- + +BindableNetworkId::BindableNetworkId (const NetworkId &rhs) : BindableInt64(rhs.getValue()) +{ +} + +// ---------------------------------------------------------------------- + +void BindableNetworkId::setValue(const NetworkId &buffer) +{ + BindableInt64::setValue(buffer.getValue()); +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/BindableNetworkId.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/BindableNetworkId.h new file mode 100644 index 00000000..2ff0dee1 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/BindableNetworkId.h @@ -0,0 +1,75 @@ +// ====================================================================== +// +// BindableNetworkId.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_BindableNetworkId_H +#define INCLUDED_BindableNetworkId_H + +// ====================================================================== + +#include "sharedDatabaseInterface/DbBindableInt64.h" +#include "sharedFoundation/NetworkId.h" + +// ====================================================================== + +namespace DB +{ + + /** + * A Bindable type for an object Id. + * This class is intended to provide a layer between NetworkId and + * the database type, so that if the implementation of NetworkId is + * changed, only this class and the database schema need to be changed. + * + * Also, this class allows the code for persisting NetworkId's to follow + * the same form as the code persisting other types. + */ + class BindableNetworkId:public BindableInt64 + { + public: + BindableNetworkId (const NetworkId &rhs); + BindableNetworkId(); + + BindableNetworkId &operator=(const NetworkId &rhs); + + void getValue(NetworkId &buffer) const; + NetworkId getValue() const; + + void setValue(const NetworkId &buffer); + }; +} + +// ====================================================================== + +inline DB::BindableNetworkId &DB::BindableNetworkId::operator=(const NetworkId &rhs) +{ + BindableInt64::setValue(rhs.getValue()); + return *this; +} + +// ---------------------------------------------------------------------- + +inline void DB::BindableNetworkId::getValue(NetworkId &buffer) const +{ + if (isNull()) + buffer.m_value = NetworkId::cms_invalid.getValue(); + else + buffer.m_value = BindableInt64::getValue(); +} + +// ---------------------------------------------------------------------- + +inline NetworkId DB::BindableNetworkId::getValue() const +{ + if (isNull()) + return NetworkId::cms_invalid; + else + return NetworkId(BindableInt64::getValue()); +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/BufferString.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/BufferString.cpp new file mode 100644 index 00000000..09c21876 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/BufferString.cpp @@ -0,0 +1,157 @@ +// ====================================================================== +// +// BufferString.cpp +// copyright (c) 2004 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/BufferString.h" + +#include "UnicodeUtils.h" +#include "sharedDatabaseInterface/DbServer.h" +#include "sharedDatabaseInterface/NullEncodedStandardString.h" +#include "sharedDatabaseInterface/NullEncodedUnicodeString.h" +#include "sharedLog/Log.h" + +using namespace DB; + +// ====================================================================== + +BufferString::BufferString(size_t maxSize) : + Bindable(), + m_value(), + m_maxSize(maxSize) +{ +} + +// ---------------------------------------------------------------------- + +void BufferString::getValue(std::string & value) const +{ + if (isNull()) + value.clear(); + else + value=m_value; +} + +// ---------------------------------------------------------------------- + +void BufferString::getValue(Unicode::String & value) const +{ + if (isNull()) + value.clear(); + else + value=Unicode::utf8ToWide(m_value); +} + +// ---------------------------------------------------------------------- + +Unicode::String BufferString::getValue() const +{ + static const Unicode::String empty; + + if (isNull()) + return empty; + else + return Unicode::utf8ToWide(m_value); +} + +// ---------------------------------------------------------------------- + +BufferString & BufferString::operator=(const std::string & value) +{ + setValue(value); + return *this; +} + +// ---------------------------------------------------------------------- + +BufferString & BufferString::operator=(const Unicode::String & value) +{ + setValue(value); + return *this; +} + +// ---------------------------------------------------------------------- + +void BufferString::setValue(const std::string & value) +{ + if (value.size() > m_maxSize) + { + if (DB::Server::getFatalOnDataError()) + { + FATAL(true,("DatabaseError: Attempt to save too long a string to the database: \"%s\"",value.c_str())); + } + else + { + WARNING_STACK_DEPTH(true,(INT_MAX,"DatabaseError: Attempt to save too long a string to the database. (Text is in the log output.)")); + LogManager::logLongText("DatabaseError",std::string("String from previous error is \"")+value+"\""); + m_value=std::string(value,0,m_maxSize); + } + } + else + m_value=value; + + indicator=m_value.size(); +} + +// ---------------------------------------------------------------------- + +void BufferString::setValue(const Unicode::String & value) +{ + setValue(Unicode::wideToUTF8(value)); +} + +// ---------------------------------------------------------------------- + +const std::string & BufferString::getValueASCII() const +{ + static const std::string empty; + + if (isNull()) + return empty; + else + return m_value; +} + +// ---------------------------------------------------------------------- + +BufferString & BufferString::operator=(const NullEncodedUnicodeString & value) +{ + return operator=(value.getValueEncoded()); +} + +// ---------------------------------------------------------------------- + +void BufferString::getValue(NullEncodedUnicodeString & value) const +{ + Unicode::String temp; + getValue(temp); + value.setValueEncoded(temp); +} + +// ---------------------------------------------------------------------- + +BufferString & BufferString::operator=(const NullEncodedStandardString & value) +{ + return operator=(value.getValueEncoded()); +} + +// ---------------------------------------------------------------------- + +void BufferString::getValue(NullEncodedStandardString & value) const +{ + std::string temp; + getValue(temp); + value.setValueEncoded(temp); +} + +// ---------------------------------------------------------------------- + +std::string BufferString::outputValue() const +{ + return m_value; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/BufferString.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/BufferString.h new file mode 100644 index 00000000..b1ae33cc --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/BufferString.h @@ -0,0 +1,80 @@ +// ====================================================================== +// +// DbBufferString.h +// copyright (c) 2004 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DbBufferString_H +#define INCLUDED_DbBufferString_H + +// ====================================================================== + +#include "sharedDatabaseInterface/DbBindableBase.h" +#include "sharedDatabaseInterface/DbBindableString.h" +#include "sharedDatabaseInterface/DbBindableUnicode.h" + +// ====================================================================== + +class NullEncodedUnicodeString; +class NullEncodedStandardString; + +// ====================================================================== + +namespace DB +{ +/** + * A nullable string class similar to DB::BindableString, but uses std::string + * internally. Unlike DB::BindableString, it does not allocate the maximum + * size for the buffer if the data is shorter. This class will save memory + * over DB::BindableString if used in a buffer that stores a lot of rows. + * However, it cannot be bound directly in a query. It must be copied + * to a varray or a DB::BindableString first. + */ + class BufferString : public Bindable + { + public: + BufferString(size_t maxSize); + template + BufferString(const BindableString & rhs) + { + rhs.getValue(m_value); + indicator=m_value.size(); + } + + template + BufferString(const BindableUnicode & rhs) + { + m_value = Unicode::wideToUTF8(rhs.getValue()); + indicator=m_value.size(); + } + + public: + void getValue(std::string & value) const; + void getValue(Unicode::String & value) const; + void getValue(NullEncodedUnicodeString & value) const; + void getValue(NullEncodedStandardString & value) const; + Unicode::String getValue() const; + const std::string & getValueASCII() const; + BufferString & operator=(const std::string & value); + BufferString & operator=(const Unicode::String & value); + BufferString & operator=(const NullEncodedUnicodeString & value); + BufferString & operator=(const NullEncodedStandardString & value); + void setValue(const std::string & value); + void setValue(const Unicode::String & value); + + virtual std::string outputValue() const; + + private: + std::string m_value; + size_t m_maxSize; + }; + + // ====================================================================== + +} // namespace + +// ====================================================================== + + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBatchQuery.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBatchQuery.h new file mode 100644 index 00000000..ba80df21 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBatchQuery.h @@ -0,0 +1,122 @@ +/* DBBatchQuery.h + * + * Templates for defining batch queries (queries that return multiple + * rows, read from the database in batches). See SQLClasses + * documentation for more details. + * + * ODBC version + */ + +#ifndef _DBBATCHQUERY_H +#define _DBBATCHQUERY_H + +#include "sharedDatabaseInterface/DbQuery.h" + +namespace DB { + +/** Template representing a Query that returns an array of data + + The template parameter R represent the structure that holds the data of one + row of the results. BatchQuery will contain an array of R's. (R will + usually be a struct holding a few Bindable types.) + */ +template +class BatchQuery : public Query +{ + R *data; + SQLUSMALLINT *rowStatus; + int numRows; + +public: + BatchQuery(int _numRows); + ~BatchQuery(); + + bool bind(); + + const R &operator[](int index) const; + R &operator[](int index); + +/** Count how many rows in the array were filled in by the last call to + Fetch(). + */ + int getNumDefinedRows() const; + +/** Is a particular row in the result array defined? A row is defined if the + database filled it in with values. If a row is not defined, the values + returned for that row by operator[] are not meaningful. + */ + bool isRowDefined(int index) const; + +/** Returns the SQL string for the query. The user must define this function; the + template does not create it. + */ + char *getSQL(); + +/** Bind the first row of data. The user must define this function; the template + does not create it. + + Implement this function as if it is binding the first element of the array + (the array is called data). The template will take care of binding the rest of + the array. It should contain something like + + data[0].member.BindCol(1,this) + */ + bool bindFirstRow(); +}; + +template +BatchQuery::BatchQuery(int _numRows) : numRows(_numRows) +{ + data = new R[numRows]; + rowStatus = new SQLUSMALLINT[numRows]; +} + +template +BatchQuery::~BatchQuery() +{ + delete[] data; + delete[] rowStatus; +} + +template +const R &BatchQuery::operator[](int index) const +{ + return data[index]; +} + +template +R &BatchQuery::operator[](int index) +{ + return data[index]; +} + +template +int BatchQuery::getNumDefinedRows() const +// TODO: hmmm, might be better to figure this once and store it +// in a mutable variable -- but how to know when it changes +// without overriding Fetch()? +{ + for (int num=numRows;num>0;--num) + { + if (isRowDefined(num)) + return num+1; + } + return 0; +} + +template +bool BatchQuery::isRowDefined(int index) const +{ + return (rowStatus[index]==0); +} + +template +bool BatchQuery::bind() +{ + if (!setArrayMode(sizeof(data[0]),numRows,rowStatus)) + return false; + return bindFirstRow(); +} + +} +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBase.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBase.cpp new file mode 100644 index 00000000..98cd481f --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBase.cpp @@ -0,0 +1,55 @@ +// ====================================================================== +// +// DBBindableBase.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbBindableBase.h" + +// ====================================================================== + +using namespace DB; + +// ---------------------------------------------------------------------- + +Bindable::Bindable() : indicator(-1) +{ +} + +// ---------------------------------------------------------------------- + +Bindable::~Bindable() +{ +} + +// ---------------------------------------------------------------------- + +Bindable::Bindable(int _indicator) : + indicator(_indicator) +{ +} + +// ---------------------------------------------------------------------- + +bool Bindable::isNull() const +{ + return (indicator==-1); +} + +// ---------------------------------------------------------------------- + +void Bindable::setNull() +{ + indicator=-1; +} + +// ---------------------------------------------------------------------- + +int *Bindable::getIndicator() +{ + return &indicator; //lint !e1536 // Exposing private member. This is done because various functions in the DB interfaces want to set the indicator directly. +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBase.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBase.h new file mode 100644 index 00000000..51008166 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBase.h @@ -0,0 +1,43 @@ +// ====================================================================== +// +// DBBindableBase.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DBBindableBase_H +#define INCLUDED_DBBindableBase_H + +// ====================================================================== + +namespace DB { + + class Bindable + { + public: + Bindable(); + //Bindable(const Bindable &rhs); // auto-generated version is fine + //Bindable &operator=(const Bindable &rhs); + virtual ~Bindable(); + + bool isNull() const; + void setNull(); + + int *getIndicator(); //TODO: enforce that only QueryImpl uses this + + virtual std::string outputValue() const = 0; // For debugging or reporting errors, output the value(s) stored in this Bindable object + + protected: + explicit Bindable(int _indicator); + + /** + * The size of the data, or -1 for NULL. + */ + int indicator; + }; + +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBitArray.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBitArray.cpp new file mode 100644 index 00000000..5f7352b6 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBitArray.cpp @@ -0,0 +1,13 @@ +// ====================================================================== +// +// DbBindableBitArray.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbBindableBitArray.h" + +// ====================================================================== + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBitArray.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBitArray.h new file mode 100644 index 00000000..a53dc71f --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBitArray.h @@ -0,0 +1,114 @@ +// ====================================================================== +// +// DBBindableBitArray.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DBBindableBitArray_H +#define INCLUDED_DBBindableBitArray_H + +// ====================================================================== + +#include "sharedDatabaseInterface/DbBindableString.h" +#include "sharedFoundation/BitArray.h" + +// ====================================================================== + +namespace DB { + + /** + * Bindable BitArray class + * + * Template parameter S is the maximum size of the BitArray + * The BitArray will be stored in the database using (S+3)/4 chars + * Each char is a hex value for 4 bits from the BitArray + */ + template + class BindableBitArray : public BindableString>2)> + { + public: + BindableBitArray(); + explicit BindableBitArray(const BitArray &bitArray); + + void setValue(const BitArray &bitArray); + BindableBitArray &operator=(const BitArray &bitArray); + + void getValue(BitArray &bitArray) const; + BitArray getValue() const; + }; + + // ---------------------------------------------------------------------- + + template + BindableBitArray::BindableBitArray() + { + } + + // ---------------------------------------------------------------------- + + template + BindableBitArray::BindableBitArray(const BitArray &bitArray) + { + setValue(bitArray); + } + + // ---------------------------------------------------------------------- + + template + void BindableBitArray::setValue(const BitArray &bitArray) + { + std::string buf; + bitArray.getAsDbTextString(buf, int((S+3)>>2)); + + // if no bits are set in the BitArray, we must + // put a character into buf, because BindableString + // won't save an empty string; empty string means + // don't overwrite current value in DB + if (buf.empty()) + buf.push_back('0'); + + BindableString>2)>::setValue(buf); + } + + // ---------------------------------------------------------------------- + + template + BindableBitArray &BindableBitArray::operator=(const BitArray &bitArray) + { + setValue(bitArray); + return *this; + } + + // ---------------------------------------------------------------------- + + template + void BindableBitArray::getValue(BitArray &bitArray) const + { + bitArray=getValue(); + } + + // ---------------------------------------------------------------------- + + template + BitArray BindableBitArray::getValue() const + { + // Each char in the string is a hex value from 0-F + char buf[((S+3)>>2) + 1]; + BindableString>2)>::getValue(buf,((S+3)>>2)); + BitArray temp; + + // indicator is the number of chars read from the database + if (indicator > 0) + { + buf[indicator] = 0; + temp.setFromDbTextString(buf); + } + + return temp; + } + +} // end namespace DB +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBool.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBool.cpp new file mode 100644 index 00000000..431b8c8e --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBool.cpp @@ -0,0 +1,70 @@ +// ====================================================================== +// +// DBBindableBool.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbBindableBool.h" + +// ====================================================================== + +using namespace DB; + +BindableBool::BindableBool() : Bindable() +{ + value[1]='\0'; +} + +BindableBool::BindableBool(bool _value) : + Bindable(1) +{ + value[0]=_value?'Y':'N'; + value[1]='\0'; +} + + +std::string BindableBool::getValueASCII() const +{ + if (isNull()) + { + return std::string(); // TODO should this be defaulted to N + } + else + { + return std::string(1,value[0]); + } +} + +bool BindableBool::getValue() const +{ + return (value[0]=='Y'); +} + +void BindableBool::setValue(bool _value) +{ + indicator=1; + value[0]=_value?'Y':'N'; +} + +BindableBool &BindableBool::operator=(bool _value) +{ + setValue(_value); + return *this; +} + +void *BindableBool::getBuffer() +{ + return value; //lint !e1536 // exposing private member (because DB interface will set it directly) +} + +std::string BindableBool::outputValue() const +{ + if (getValue()) + return std::string("true"); + else + return std::string("false"); +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBool.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBool.h new file mode 100644 index 00000000..36929d60 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableBool.h @@ -0,0 +1,58 @@ +// ====================================================================== +// +// DBBindableBool.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DBBindableBool_H +#define INCLUDED_DBBindableBool_H + +// ====================================================================== + +#include "sharedDatabaseInterface/DbBindableBase.h" +#include + +// ====================================================================== + +namespace DB { + +/** Represents a bool in C++, bound to a CHAR(1) column. The column can have the values 'Y' or 'N'. + * TODO: use a boolean type in the database, if we can find one that allows NULL's and is fairly + * portable. (May not exist -- according to the docs, Oracle does not have a boolean datatype.) + */ + class BindableBool : public Bindable + { + char value[2]; + public: + explicit BindableBool(bool _value); + BindableBool(); + + std::string getValueASCII() const; + bool getValue() const; + void getValue(bool &buffer) const; + void setValue(bool _value); + BindableBool &operator=(bool _value); + + void *getBuffer(); + + virtual std::string outputValue() const; + }; //lint !e1721 !e1509 // no virtual destructor, unusual operator = + +} + +// ---------------------------------------------------------------------- + +/** + * There are places where it's more convenient to have a getValue() that + * takes a reference, for consistancy with more complicated types. + */ +inline void DB::BindableBool::getValue(bool &buffer) const +{ + buffer=getValue(); +} + + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableDouble.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableDouble.cpp new file mode 100644 index 00000000..438ea6ec --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableDouble.cpp @@ -0,0 +1,83 @@ +// ====================================================================== +// +// DBBindableDouble.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbBindableDouble.h" + +#include "sharedDatabaseInterface/DbServer.h" + +#include + +// ====================================================================== + +using namespace DB; + +BindableDouble::BindableDouble() : Bindable(), value(-999.0) +{ +} + +BindableDouble::BindableDouble(const double &_value) : + Bindable(sizeof(value)), + value(_value) +{ + if (finite(value)==0) + { + if (DB::Server::getFatalOnDataError()) + { + FATAL(true,("DatabaseError: Attempt to save a non-finite value to the database.")); + } + else + { + value=0; + WARNING_STACK_DEPTH(true,(INT_MAX,"DatabaseError: Attempt to save a non-finite value to the database.")); + } + } +} + +double BindableDouble::getValue() const +{ + return value; +} + +void BindableDouble::setValue(const double &_value) +{ + indicator=sizeof(value); + value=_value; + if (finite(value)==0) + { + if (DB::Server::getFatalOnDataError()) + { + FATAL(true,("DatabaseError: Attempt to save a non-finite value to the database.")); + } + else + { + value=0; + WARNING_STACK_DEPTH(true,(INT_MAX,"DatabaseError: Attempt to save a non-finite value to the database.")); + } + } +} + +BindableDouble &BindableDouble::operator=(const double &_value) +{ + setValue(_value); + return *this; +} + +void *BindableDouble::getBuffer() +{ + return &value; //lint !e1536 // exposing private member (because DB interface will set it directly) +} + +std::string BindableDouble::outputValue() const +{ + char temp[255]; + snprintf(temp,sizeof(temp),"%f",value); + temp[sizeof(temp)-1]='\0'; + return std::string(temp); +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableDouble.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableDouble.h new file mode 100644 index 00000000..d743616b --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableDouble.h @@ -0,0 +1,61 @@ +// ====================================================================== +// +// DBBindableDouble.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DBBindableDouble_H +#define INCLUDED_DBBindableDouble_H + +// ====================================================================== + +#include "sharedDatabaseInterface/DbBindableBase.h" + +// ====================================================================== + +namespace DB { + + /** + * Represents a double in C++, bound to a FLOAT(nn) or a NUMBER database column + * TODO: make variations of this for different precisions (when it is determined what precisions we need) + */ + class BindableDouble : public Bindable + { + double value; + public: + explicit BindableDouble(const double &_value); + BindableDouble(); + + double getValue() const; + void setValue(const double &_value); + BindableDouble &operator=(const double &_value); + + void getValue(double &buffer) const; + void getValue(real &buffer) const; + + void *getBuffer(); + + virtual std::string outputValue() const; + + }; //lint !e1721 !e1509 // no virtual destructor, unusual operator = + +} + +// ---------------------------------------------------------------------- + +inline void DB::BindableDouble::getValue(double &buffer) const +{ + buffer=getValue(); +} + +// ---------------------------------------------------------------------- + +inline void DB::BindableDouble::getValue(real &buffer) const +{ + buffer=static_cast(getValue()); +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableInt64.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableInt64.cpp new file mode 100644 index 00000000..745a1f5d --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableInt64.cpp @@ -0,0 +1,56 @@ +// ====================================================================== +// +// DBBindableInt64.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbBindableInt64.h" + +#include + +// ====================================================================== + +using namespace DB; + +BindableInt64::BindableInt64() +{ +} + +// ---------------------------------------------------------------------- + +BindableInt64::BindableInt64(int64 value) +{ + setValue(value); +} + +// ---------------------------------------------------------------------- + +int64 BindableInt64::getValue() const +{ + int64 temp; + char buffer[BindableInt64BufferSize+1]; + BindableString::getValue(buffer,BindableInt64BufferSize+1); + sscanf(buffer,INT64_FORMAT_SPECIFIER,&temp); + return temp; +} + +// ---------------------------------------------------------------------- + +BindableInt64 &BindableInt64::operator=(int64 rhs) +{ + setValue(rhs); + return *this; +} + +// ---------------------------------------------------------------------- + +void BindableInt64::setValue(int64 rhs) +{ + char buffer[BindableInt64BufferSize+1]; + sprintf(buffer,INT64_FORMAT_SPECIFIER,rhs); + BindableString::setValue(buffer); +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableInt64.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableInt64.h new file mode 100644 index 00000000..a9a1b630 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableInt64.h @@ -0,0 +1,56 @@ +// ====================================================================== +// +// DBBindableInt64.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DBBindableInt64_H +#define INCLUDED_DBBindableInt64_H + +// ====================================================================== + +#include "sharedDatabaseInterface/DbBindableString.h" + +// ====================================================================== + +#define BindableInt64BufferSize 21 // big enough to hold -(2^63) + +namespace DB { + + /** + * Represents an int64 in C++ + */ + class BindableInt64 : public BindableString + { + public: + BindableInt64(); + explicit BindableInt64(int64 value); + + int64 getValue() const; + void setValue(const int64 rhs); + BindableInt64 &operator=(const int64 rhs); + + void getValue(int64 &buffer) const; + void getValue(uint64 &buffer) const; + }; + +} + +// ---------------------------------------------------------------------- + +inline void DB::BindableInt64::getValue(int64 &buffer) const +{ + buffer=getValue(); +} + +// ---------------------------------------------------------------------- + +inline void DB::BindableInt64::getValue(uint64 &buffer) const +{ + buffer=static_cast(getValue()); +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableLong.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableLong.cpp new file mode 100644 index 00000000..2aa0818e --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableLong.cpp @@ -0,0 +1,64 @@ +// ====================================================================== +// +// DBBindableLong.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbBindableLong.h" + +#include + +// ====================================================================== + +using namespace DB; + +BindableLong::BindableLong() : Bindable(), value(-999) +{ +} + +BindableLong::BindableLong(long int _value) : Bindable(sizeof(value)), value(_value) +{ +} + +void *BindableLong::getBuffer() +{ + return &value; //lint !e1536 // exposing private member +} + +// ---------------------------------------------------------------------- + +long int BindableLong::getValue() const +{ + return value; +} + +// ---------------------------------------------------------------------- + +BindableLong &BindableLong::operator=(long int rhs) +{ + indicator=sizeof(value); + value=rhs; + return *this; +} + +// ---------------------------------------------------------------------- + +void BindableLong::setValue(long int rhs) +{ + indicator=sizeof(value); + value=rhs; +} + +// ---------------------------------------------------------------------- + +std::string BindableLong::outputValue() const +{ + char temp[255]; + snprintf(temp,sizeof(temp),"%li",value); + temp[sizeof(temp)-1]='\0'; + return std::string(temp); +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableLong.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableLong.h new file mode 100644 index 00000000..f4dc519c --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableLong.h @@ -0,0 +1,95 @@ +// ====================================================================== +// +// DBBindableLong.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DBBindableLong_H +#define INCLUDED_DBBindableLong_H + +// ====================================================================== + +#include "sharedDatabaseInterface/DbBindableBase.h" + +// ====================================================================== + +namespace DB { + + /** + * Represents a long in C++, bound to an INTEGER or NUMBER database column + */ + class BindableLong : public Bindable + { + public: + BindableLong(); + explicit BindableLong(long int _value); + + long int getValue() const; + void setValue(const long int rhs); + BindableLong &operator=(const long int rhs); + + // following alternate getValue's are provided for convenience, particularly in + // the auto-generated code: + void getValue(unsigned int &buffer) const; + void getValue(uint32 &buffer) const; // for some reason, our compiler is convinced that uint32 != unsigned int + void getValue(long int &buffer) const; + void getValue(int &buffer) const; + void getValue(int8 &buffer) const; + void getValue(uint8 &buffer) const; + + void *getBuffer(); + + virtual std::string outputValue() const; + + private: + long int value; + }; //lint !e1721 !e1509 // no virtual destructor, unusual operator = + +} + +// ---------------------------------------------------------------------- + +inline void DB::BindableLong::getValue(unsigned int &buffer) const +{ + buffer=static_cast(getValue()); +} + +// ---------------------------------------------------------------------- + +inline void DB::BindableLong::getValue(uint32 &buffer) const +{ + buffer=static_cast(getValue()); +} + +// ---------------------------------------------------------------------- + +inline void DB::BindableLong::getValue(long int &buffer) const +{ + buffer=getValue(); +} + +// ---------------------------------------------------------------------- + +inline void DB::BindableLong::getValue(int &buffer) const +{ + buffer=getValue(); +} + +// ---------------------------------------------------------------------- + +inline void DB::BindableLong::getValue(int8 &buffer) const +{ + buffer=static_cast(getValue()); +} + +// ---------------------------------------------------------------------- + +inline void DB::BindableLong::getValue(uint8 &buffer) const +{ + buffer=static_cast(getValue()); +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableString.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableString.cpp new file mode 100644 index 00000000..fd8d3792 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableString.cpp @@ -0,0 +1,19 @@ +// ====================================================================== +// +// DBBindableString.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbBindableString.h" + +// ====================================================================== + +std::string DB::BindableStringBase::outputValue() const +{ + const std::string quote("\""); + return quote + std::string(m_buffer) + quote; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableString.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableString.h new file mode 100644 index 00000000..0d467705 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableString.h @@ -0,0 +1,326 @@ +// ====================================================================== +// +// DBBindableString.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DBBindableString_H +#define INCLUDED_DBBindableString_H + +// ====================================================================== + +#include "sharedDatabaseInterface/DbBindableBase.h" + +#include "UnicodeUtils.h" + +// ====================================================================== + +namespace DB +{ + /** + * A large instrument resembling a cello. + */ + class BindableStringBase : public Bindable + { + public: + + ~BindableStringBase(); + + void *getBuffer(); + int getS(); + + virtual std::string outputValue() const; + + protected: + + BindableStringBase(char *buffer, int s); + BindableStringBase(char *buffer, int s, const BindableStringBase &rhs); + void operator=(const BindableStringBase &rhs); + + private: + + BindableStringBase(); + BindableStringBase(const BindableStringBase &rhs); // disable + + private: + + char *m_buffer; + int m_s; + }; + + inline BindableStringBase::BindableStringBase(char *buffer, int s) : + m_buffer(buffer), + m_s(s) + { + } + + /** + * This takes the place of a copy contructor. + * It gets buffer and s from the derived class, instead of from rhs. + */ + inline BindableStringBase::BindableStringBase(char *buffer, int s, const BindableStringBase &rhs) : + Bindable(rhs), + m_buffer(buffer), + m_s(s) + { + DEBUG_FATAL(m_s!=rhs.m_s,("Cannot use BindableString copy constructor on different size strings.\n")); // compiler shouldn't allow this, but just to make sure... + } + + inline BindableStringBase::~BindableStringBase() + { + m_buffer = 0; + } + + inline void *BindableStringBase::getBuffer() + { + return m_buffer; + } + + inline int BindableStringBase::getS() + { + return m_s; + } + + inline void BindableStringBase::operator=(const BindableStringBase &rhs) + { + //does not copy m_buffer or m_s + DEBUG_FATAL(m_s!=rhs.m_s,("Cannot use BindableString::operator= on different size strings.\n")); // compiler shouldn't allow this, but just to make sure... + Bindable::operator=(rhs); + } + +// ====================================================================== +/** Template that allows creating a class representing a C++ char[] array + bound to a VARCHAR in the database. + + BindableString is a template so that the size can be specified when it + is instantiated. The template parameter S represents the size of the + array. + + Question: Why is this a template? Couldn't S be a parameter to the + constructor? + + Short answer: Because of the way ODBC works, the size must be known at + compile time + + Long answer: We want to be able to use these things in arrays that are + bound. The functions for binding an array do not allow an element of the + array to be a pointer to another location. Therefore, to work with ODBC, + the buffer for the string must be part of the class, not a pointer. + + TODO: Currently this template represents 8-bit characters. Make it support + Unicode. + TODO: We have both setValue() functions and operator='s. Should just pick + one and use it. + */ + template + class BindableString : public BindableStringBase + { + public: + BindableString(); + explicit BindableString(const Unicode::String &_value); + explicit BindableString(const std::string &_value); + BindableString(const BindableString &rhs); + +/** Copy the string to another string + */ + char *getValue(char *buffer, size_t bufsize) const; +/** Copy the string to a Unicode String class + */ + Unicode::String getValue() const; +/** Copy the string to an 8-bit String class + */ + std::string getValueASCII() const; + +/** Allocate a new string and copy the buffer to it. The caller takes responsibility + for delete[]ing it later. (If the string is NULL, this returns a null pointer.) + */ + char *makeCopyOfValue() const; + +/** Set the value of the bindable string from another (null-terminated) string + + If the source buffer is too big, it FATAL's. Another alternative would be to have + it truncate the string. Right now, it's better to FATAL to avoid hard-to-find bugs + caused by the database fields being too small. However, might want to revist this + later. + */ + void setValue(const char *buffer); + +/** Set the value of the bindable string from a String class. See also setValue(const char*). + */ + void setValue(const Unicode::String &buffer); + +/** + * Set the value of the bindable string from a std::string. + */ + void setValue(const std::string &buffer); + +/** Same as setValue + */ + BindableString &operator=(const Unicode::String &buffer); + BindableString &operator=(const std::string &buffer); + +// Alternate forms of getValue, useful for auto-generated code (because of type conversion issues) + void getValue(Unicode::String &buffer) const; + void getValue(std::string &buffer) const; + + int getS() const; + + private: + + char m_value[S+1]; // column of size S, plus one byte for a trailing null + }; + +// ====================================================================== +// Template functions + + template + BindableString::BindableString() + : BindableStringBase(m_value, S) + { + } + + template + BindableString::BindableString(const Unicode::String &value) + : BindableStringBase(m_value, S) + { + setValue(value); + } + + template + BindableString::BindableString(const std::string &value) + : BindableStringBase(m_value, S) + { + setValue(value); + } + + template + BindableString::BindableString(const BindableString &rhs) + : BindableStringBase(m_value, S, rhs) + { + strncpy(m_value,rhs.m_value,S+1); + } + + template + char *BindableString::getValue(char *buffer, size_t bufsize) const + { + if (isNull()) + { + return strncpy(buffer,"\0",1); // in the database, a zero-length string and a NULL aren't really + // the same thing, but this is the closest we can do here. + } + return strncpy(buffer,m_value,(bufsize>(S+1))?(S+1):bufsize); + } + + template + Unicode::String BindableString::getValue() const + { + if (isNull()) + { + return Unicode::String(); + } + else + { + //-- Justin Randall [4/11/2001 5:56:56 PM] -- + // @todo : value is currently a char, should be an short or Unicode::String::value_type or wchar_t + return Unicode::String(Unicode::narrowToWide(m_value)); + } + } + + template + std::string BindableString::getValueASCII() const + { + if (isNull()) + { + return std::string(); + } + else + { + return std::string(m_value); + } + } + + template + void BindableString::setValue(const char *buffer) + { + if (buffer==0) + { + setNull(); + return; + } + + // the following is strncpy, but keeps a count of the number of characters + unsigned int i; + for (i=0;i + void BindableString::setValue(const Unicode::String &buffer) + { + FATAL(buffer.size()>S,("Attempt to save a Unicode::String \"%s\"that is too long to the database.", Unicode::wideToNarrow(buffer).c_str())); + strncpy(m_value, Unicode::wideToNarrow(buffer).c_str(), S+1); + indicator=buffer.size(); + } + + template + void BindableString::setValue(const std::string &buffer) + { + FATAL(strlen(buffer.c_str())>S,("Attempt to save a std::string \"%s\"that is too long to the database.", buffer.c_str())); + strncpy(m_value, buffer.c_str(), S+1); + indicator=strlen(buffer.c_str()); + } + + template + BindableString &BindableString::operator=(const Unicode::String &buffer) + { + setValue(buffer); + return *this; + } + + template + BindableString &BindableString::operator=(const std::string &buffer) + { + setValue(buffer.c_str()); + return *this; + } + + template + char *BindableString::makeCopyOfValue() const + { + if (isNull()) return 0; + + char *target=new char[indicator+1]; + strncpy(target,m_value,indicator+1); + return target; + } + + template + void BindableString::getValue(Unicode::String &buffer) const + { + buffer=getValue(); + } + + template + void BindableString::getValue(std::string &buffer) const + { + buffer=getValueASCII(); + } + + template + int BindableString::getS() const + { + return S; + } + +// ====================================================================== +} + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableUnicode.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableUnicode.cpp new file mode 100644 index 00000000..4817ecc4 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableUnicode.cpp @@ -0,0 +1,19 @@ +// ====================================================================== +// +// DBBindableUnicode.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbBindableUnicode.h" + +// ====================================================================== + +std::string DB::BindableUnicodeBase::outputValue() const +{ + const std::string quote("\""); + return quote + std::string(m_buffer) + quote; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableUnicode.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableUnicode.h new file mode 100644 index 00000000..9c85e6d4 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBindableUnicode.h @@ -0,0 +1,226 @@ +// ====================================================================== +// +// DBBindableUnicode.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DBBindableUnicode_H +#define INCLUDED_DBBindableUnicode_H + +// ====================================================================== + +#include "sharedDatabaseInterface/DbBindableBase.h" + +#include "UnicodeUtils.h" + +// ====================================================================== + +namespace DB +{ + /** + * A large instrument resembling a cello. + */ + class BindableUnicodeBase : public Bindable + { + public: + + ~BindableUnicodeBase(); + + void *getBuffer(); + int getS(); + + virtual std::string outputValue() const; + + protected: + + BindableUnicodeBase(char *buffer, int s); + BindableUnicodeBase(char *buffer, int s, const BindableUnicodeBase &rhs); + void operator=(const BindableUnicodeBase &rhs); + + private: + + BindableUnicodeBase(); + BindableUnicodeBase(const BindableUnicodeBase &rhs); // disable + + private: + + char *m_buffer; + int m_s; + }; + + inline BindableUnicodeBase::BindableUnicodeBase(char *buffer, int s) : + m_buffer(buffer), + m_s(s) + { + } + + /** + * This takes the place of a copy contructor. + * It gets buffer and s from the derived class, instead of from rhs. + */ + inline BindableUnicodeBase::BindableUnicodeBase(char *buffer, int s, const BindableUnicodeBase &rhs) : + Bindable(rhs), + m_buffer(buffer), + m_s(s) + { + DEBUG_FATAL(m_s!=rhs.m_s,("Cannot use BindableUnicode copy constructor on different size strings.\n")); // compiler shouldn't allow this, but just to make sure... + } + + inline BindableUnicodeBase::~BindableUnicodeBase() + { + m_buffer = 0; + } + + inline void *BindableUnicodeBase::getBuffer() + { + return m_buffer; + } + + inline int BindableUnicodeBase::getS() + { + return m_s; + } + + inline void BindableUnicodeBase::operator=(const BindableUnicodeBase &rhs) + { + //does not copy m_buffer or m_s + DEBUG_FATAL(m_s!=rhs.m_s,("Cannot use BindableUnicode::operator= on different size strings.\n")); // compiler shouldn't allow this, but just to make sure... + Bindable::operator=(rhs); + } + +// ====================================================================== +/** Template that allows creating a class representing a C++ char[] array + bound to a VARCHAR in the database. + + BindableUnicode is a template so that the size can be specified when it + is instantiated. The template parameter S represents the size of the + array. + + Question: Why is this a template? Couldn't S be a parameter to the + constructor? + + Short answer: Because of the way ODBC works, the size must be known at + compile time + + Long answer: We want to be able to use these things in arrays that are + bound. The functions for binding an array do not allow an element of the + array to be a pointer to another location. Therefore, to work with ODBC, + the buffer for the string must be part of the class, not a pointer. + + TODO: Currently this template represents 8-bit characters. Make it support + Unicode. + TODO: We have both setValue() functions and operator='s. Should just pick + one and use it. + */ + template + class BindableUnicode : public BindableUnicodeBase + { + public: + BindableUnicode(); + explicit BindableUnicode(const Unicode::String &_value); + BindableUnicode(const BindableUnicode &rhs); + +/** Copy the string to another string + */ + //char *getValue(char *buffer, size_t bufsize) const; +/** Copy the string to a Unicode String class + */ + Unicode::String getValue() const; + +/** Set the value of the bindable string from a String class. See also setValue(const char*). + */ + void setValue(const Unicode::String &buffer); + +/** Same as setValue + */ + BindableUnicode &operator=(const Unicode::String &buffer); + +// Alternate forms of getValue, useful for auto-generated code (because of type conversion issues) + void getValue(Unicode::String &buffer) const; + + int getS() const; + + private: + + char m_value[S+1]; // column of size S, plus one byte for a trailing null + }; + +// ====================================================================== +// Template functions + + template + BindableUnicode::BindableUnicode() + : BindableUnicodeBase(m_value, S) + { + } + + template + BindableUnicode::BindableUnicode(const Unicode::String &value) + : BindableUnicodeBase(m_value, S) + { + setValue(value); + } + + template + BindableUnicode::BindableUnicode(const BindableUnicode &rhs) + : BindableUnicodeBase(m_value, S, rhs) + { + strncpy(m_value,rhs.m_value,S+1); + } + + template + Unicode::String BindableUnicode::getValue() const + { + if (isNull()) + { + return Unicode::String(); + } + else + { + Unicode::String str; + std::basic_string cstr (m_value, m_value + indicator); + str = Unicode::utf8ToWide(cstr); + return str; + + //-- Justin Randall [4/11/2001 5:56:56 PM] -- + // @todo : value is currently a char, should be an short or Unicode::String::value_type or wchar_t + //return Unicode::String(Unicode::narrowToWide(m_value)); + } + } + + template + void BindableUnicode::setValue(const Unicode::String &buffer) + { + FATAL(buffer.size()>S,("Attempt to save a Unicode::String \"%s\"that is too long to the database.", Unicode::wideToNarrow(buffer).c_str())); + + std::string str; + str = Unicode::wideToUTF8(buffer, str); + indicator = (str.size()>S ? S : str.size()); + memcpy(m_value, str.c_str(),indicator); + m_value[indicator] = '\0'; + } + + template + BindableUnicode &BindableUnicode::operator=(const Unicode::String &buffer) + { + setValue(buffer); + return *this; + } + + template + void BindableUnicode::getValue(Unicode::String &buffer) const + { + buffer.assign(getValue()); + } + + template + int BindableUnicode::getS() const + { + return S; + } + +// ====================================================================== +} + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBufferRow.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBufferRow.cpp new file mode 100644 index 00000000..abc60519 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBufferRow.cpp @@ -0,0 +1,13 @@ +// ====================================================================== +// +// DBBufferRow.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbBufferRow.h" + +// ====================================================================== + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBufferRow.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBufferRow.h new file mode 100644 index 00000000..0d1b70d0 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbBufferRow.h @@ -0,0 +1,53 @@ +// ====================================================================== +// +// DBBufferRow.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +//TODO: Is this file obsolete? + +#ifndef INCLUDED_DBBufferRow_H +#define INCLUDED_DBBufferRow_H + +// ====================================================================== + +#include "sharedDatabaseInterface/DbRow.h" + +// ====================================================================== + +namespace DB +{ + + /** + * A BufferRow is a Row that works with the TableBuffer class. + * + * A BufferRow is a Row that represents a Row from a table (not a view + * or a query that joins multiple tables). It implements various + * virtual functions that the TableBuffer class uses. + */ + struct BufferRow:public Row + { + + /** + * Combines the values in this row with the values in newRow. + * + * For each column: + * If the column is null in newRow, do nothing. (Leave the value + * in this row unchanged.) + * If the column is not null in newRow, replace the value in this + * row with the value in newRow. + */ + virtual void combine(const DB::Row &newRow) =0; + + /** + * Makes a new copy of this row. + */ + virtual BufferRow *copy(void) const =0; + }; + +} // namespace + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbException.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbException.cpp new file mode 100644 index 00000000..045234de --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbException.cpp @@ -0,0 +1,33 @@ +// ====================================================================== +// +// DbException.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbException.h" + +// ====================================================================== + +using namespace DB; + +// ====================================================================== + +Exception::Exception(int errorNumber, const std::string &errorDescription, const std::string &contextDescription) : + m_errorNumber(errorNumber), + m_errorDescription(errorDescription), + m_contextDescription(contextDescription) +{ +} + +// ---------------------------------------------------------------------- + +Exception::Exception(int errorNumber, const std::string &errorDescription) : + m_errorNumber(errorNumber), + m_errorDescription(errorDescription), + m_contextDescription() +{ +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbException.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbException.h new file mode 100644 index 00000000..f408564f --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbException.h @@ -0,0 +1,62 @@ +// ====================================================================== +// +// DbException.h +// copyright (c) 2003 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DbException_H +#define INCLUDED_DbException_H + +// ====================================================================== + +#include +// ====================================================================== + +namespace DB +{ + class Exception + { + public: + Exception(int errorNumber, const std::string &errorDescription, const std::string &contextDescription); + Exception(int errorNumber, const std::string &errorDescription); + + public: + int getErrorNumber() const; + const std::string & getErrorDescription() const; + const std::string & getContextDescription() const; + + private: + int m_errorNumber; + std::string m_errorDescription; + std::string m_contextDescription; + }; + + // ====================================================================== + + inline int Exception::getErrorNumber() const + { + return m_errorNumber; + } + + // ---------------------------------------------------------------------- + + inline const std::string & Exception::getErrorDescription() const + { + return m_errorDescription; + } + + // ---------------------------------------------------------------------- + + inline const std::string & Exception::getContextDescription() const + { + return m_contextDescription; + } + + // ====================================================================== + +} // namespace + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbModeQuery.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbModeQuery.cpp new file mode 100644 index 00000000..eaf2833d --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbModeQuery.cpp @@ -0,0 +1,19 @@ +// ====================================================================== +// +// DBModeQuery.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbModeQuery.h" + +// ====================================================================== + +DB::ModeQuery::~ModeQuery() +{ + delete data; +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbModeQuery.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbModeQuery.h new file mode 100644 index 00000000..2e6a003c --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbModeQuery.h @@ -0,0 +1,120 @@ +/* DBModeQuery.h + * + * Query objects that support different modes of operation + * + */ + +#ifndef _DBMODEQUERY_H +#define _DBMODEQUERY_H + +//----------------------------------------------------------------------- + +#include "sharedDatabaseInterface/DbQuery.h" +#include "sharedDatabaseInterface/DbRow.h" + +//----------------------------------------------------------------------- + +namespace DB +{ + +/** + * A ModeQuery is a Query that can be in update, insert, select, or delete mode. + * + * This class is designed to work with the TableBuffer class. + * + * Derive from this class to make a query for a specific purpose. + * Implement the following in the derived class: + * 1) A constructor that initializes _data_ to point to a row of the appropriate type. + * 2) A bind function (will need to dynamic_cast _data_ to the appropriate type of row) + * 3) A getSQL() function + * + * Note that the copy() function must be defined for the type of row used. + * + * Note also that getSQL() will need to return different values depending on the mode. Bind() + * may also need to behave differently in different modes, depending on the query. The default + * destructor deletes _data_, so it is not necessary to do so in a derived destructor. + */ +class ModeQuery : public Query +{ + public: + enum Mode {mode_UPDATE,mode_INSERT,mode_DELETE,mode_SELECT,mode_INVALID}; + Mode mode; //TODO: rename to m_mode + + protected: + Row *data; + + public: + explicit ModeQuery(Row *derivedRow); + virtual ~ModeQuery(); + + void setData(const Row &_data); + const Row &getData(void) const; + + void updateMode(); + void insertMode(); + void deleteMode(); + void selectMode(); + + private: + ModeQuery(); //disable + ModeQuery(const ModeQuery &); //disable + ModeQuery & operator=(const ModeQuery &); //disable +}; + +//----------------------------------------------------------------------- +// Inline functions + +inline ModeQuery::ModeQuery(Row *derivedRow) : + Query(), + mode (mode_INVALID), + data(derivedRow) +{ +} + +//------------------------------------------------------------------------ + +inline void ModeQuery::insertMode() +{ + mode=mode_INSERT; +} + +//------------------------------------------------------------------------ + +inline void ModeQuery::updateMode() +{ + mode=mode_UPDATE; +} + +//------------------------------------------------------------------------ + +inline void ModeQuery::deleteMode() +{ + mode=mode_DELETE; +} + +//------------------------------------------------------------------------ + +inline void ModeQuery::selectMode() +{ + mode=mode_SELECT; +} + +//------------------------------------------------------------------------ + +inline const Row &ModeQuery::getData() const +{ + return *data; +} + +// ---------------------------------------------------------------------- + +inline void ModeQuery::setData(const Row &_data) +{ + data->copy(_data); +} + +// ---------------------------------------------------------------------- + +} // namespace + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbProfiler.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbProfiler.cpp new file mode 100644 index 00000000..343e6aec --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbProfiler.cpp @@ -0,0 +1,70 @@ +// ====================================================================== +// +// DbProfiler.cpp +// copyright (c) 2003 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbProfiler.h" + +#include +#include +#include "sharedLog/Log.h" + +// ====================================================================== + +namespace DB +{ + + Profiler::Profiler() : + m_queryDataMap(new QueryDataMapType) + { + } + +// ---------------------------------------------------------------------- + + Profiler::~Profiler() + { + delete m_queryDataMap; + m_queryDataMap = 0; + } + +// ---------------------------------------------------------------------- + + void Profiler::addQueryProfileData(const std::string &queryName, int execTime, int fetchTime, int execCount, int fetchCount) + { + QueryData &data = (*m_queryDataMap)[queryName]; + data.m_execTime += execTime; + data.m_fetchTime += fetchTime; + data.m_execCount += execCount; + data.m_fetchCount += fetchCount; + } + +// ---------------------------------------------------------------------- + + void Profiler::debugOutput() + { +// DEBUG_REPORT_LOG(true,("Query profile:\n")); + for (QueryDataMapType::const_iterator i=m_queryDataMap->begin(); i!=m_queryDataMap->end(); ++i) + { + LOG("QueryProfile",("\t%s\t%i ms/exec (%i execs in %i ms, %i fetches in %i ms)\n", + i->first.c_str(), (i->second.m_execTime / i->second.m_execCount), i->second.m_execCount, + i->second.m_execTime, i->second.m_fetchCount, i->second.m_fetchTime)); + } +// DEBUG_REPORT_LOG(true,("End query profile\n")); + } + +// ---------------------------------------------------------------------- + + Profiler::QueryData::QueryData() : + m_execTime(0), + m_fetchTime(0), + m_execCount(0), + m_fetchCount(0) + { + } + +} // namespace + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbProfiler.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbProfiler.h new file mode 100644 index 00000000..be5e9f03 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbProfiler.h @@ -0,0 +1,43 @@ +// ====================================================================== +// +// DbProfiler.h +// copyright (c) 2003 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DbProfiler_H +#define INCLUDED_DbProfiler_H + +// ====================================================================== + +namespace DB +{ + class Profiler + { + public: + Profiler(); + ~Profiler(); + void addQueryProfileData(const std::string &queryName, int execTime, int fetchTime, int execCount, int fetchCount); + void debugOutput(); + + private: + class QueryData + { + public: + QueryData(); + + public: + int m_execTime; + int m_fetchTime; + int m_execCount; + int m_fetchCount; + }; + + typedef stdmap::fwd QueryDataMapType; + QueryDataMapType *m_queryDataMap; + }; + +} //namespace +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbProtocol.def b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbProtocol.def new file mode 100644 index 00000000..9807406a --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbProtocol.def @@ -0,0 +1,25 @@ +// ====================================================================== +// +// DBProtocol.def +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DBProtocol_DEF +#define INCLUDED_DBProtocol_DEF + +// ====================================================================== + +namespace DB +{ + const int PROTOCOL_DEFAULT=0; + const int PROTOCOL_ODBC=1; + const int PROTOCOL_OCI=2; + + typedef int Protocol; + +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbQuery.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbQuery.cpp new file mode 100644 index 00000000..f78c6b2e --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbQuery.cpp @@ -0,0 +1,334 @@ +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbQuery.h" + +#include "sharedDatabaseInterface/DbException.h" +#include "sharedDatabaseInterface/DbQueryImplementation.h" +#include "sharedDatabaseInterface/DbServer.h" +#include "sharedDatabaseInterface/DbSession.h" +#include "sharedFoundation/Clock.h" +#include "sharedLog/Log.h" + +#include + +using namespace DB; + +namespace DBQueryNamespace +{ + void logLargeText(std::string const & logSection, std::string const & text); +} + +Query::Query() : + impl(0), + m_session(0), + inUse(false), + m_execTime(0), + m_fetchTime(0), + m_execCount(0), + m_fetchCount(0), + m_rowsFetched(0), + m_debugName(0), + m_queryMode(MODE_SQL) +{ +} + +Query::~Query() +{ + if (inUse) + done(); + + delete impl; + delete m_debugName; + impl = 0; + m_debugName = 0; +} //lint !e1740 // dtor doesn't delete m_session (because we don't own it) + +int Query::fetch() +{ + int startTime = 0; + if (Server::isProfilingEnabled()) + startTime = Clock::timeMs(); + + NOT_NULL(impl); + m_rowsFetched = impl->fetch(); + + if (Server::isProfilingEnabled()) + { + m_fetchTime += Clock::timeMs() - startTime; + } + if (m_rowsFetched >= 0) + { + m_fetchCount += m_rowsFetched; + return m_rowsFetched; + } + else + return -1; +} + +void Query::done() +{ + if (inUse) // so that it's safe to call Done() on a query that hasn't been used + { + NOT_NULL(impl); + NOT_NULL(m_session); + + impl->done(); + m_session->queryDone(); + m_session->addQueryCount(m_execCount, m_fetchCount); + if (Server::isProfilingEnabled()) + { + NOT_NULL(m_debugName); // should have been set by setup() + Server::addQueryProfileData(*m_debugName, m_execTime, m_fetchTime, m_execCount, m_fetchCount); + + // reset the profiler data in case we run this query again: + m_execTime=0; + m_fetchTime=0; + m_execCount=0; + m_fetchCount=0; + } + inUse=false; + m_session=0; + } +} + +// ---------------------------------------------------------------------- + +/** + * Returns the number of rows affected by the most recent query. (Only applies + * to DML queries.) + */ +int Query::rowCount() +{ + if (!inUse) + { + return 0; + } + else + { + NOT_NULL(impl); + return impl->rowCount(); + } +} + +// ---------------------------------------------------------------------- + +bool Query::setup(Session *session) +{ + if (impl == 0) + { + impl=session->createQueryImpl(this); + } + NOT_NULL(impl); + NOT_NULL(session); + m_session=session; + + if (Server::isProfilingEnabled() && !m_debugName) + { + m_debugName = new std::string; + getDebugName(*m_debugName); + } + + if (impl->setup(session)) + { + inUse=true; + return true; + } + else + return false; +} + +bool Query::prepare() +{ + NOT_NULL(impl); + return impl->prepare(); +} + +bool Query::exec() +{ + int startTime = 0; + if (Server::isProfilingEnabled()) + startTime = Clock::timeMs(); + + m_queryMode=getExecutionMode(); + + NOT_NULL(impl); + if (Server::isVerboseModeEnabled()) + { + std::string context; + getDebugName(context); + DEBUG_REPORT_LOG(true,("query: %s\n",context.c_str())); + } + bool result = impl->exec(); + + if (Server::isProfilingEnabled()) + { + m_execTime += Clock::timeMs() - startTime; + } + ++m_execCount; + + if (! result) + { + std::string data = outputDataValues(); + LOG("DatabaseError",("Data from previous error:")); + DBQueryNamespace::logLargeText("DatabaseError", data); + done(); + } + return result; +} + +void Query::setColArrayMode(size_t skipSize, size_t numElements) +{ + NOT_NULL(impl); + impl->setColArrayMode(skipSize, numElements); +} + +bool Query::bindCol(BindableLong &buffer) +{ + NOT_NULL(impl); + return impl->bindCol(buffer); +} + +bool Query::bindParameter(BindableLong &buffer) +{ + NOT_NULL(impl); + return impl->bindParameter(buffer); +} + +bool Query::bindCol(BindableDouble &buffer) +{ + NOT_NULL(impl); + return impl->bindCol(buffer); +} + +bool Query::bindParameter(BindableDouble &buffer) +{ + NOT_NULL(impl); + return impl->bindParameter(buffer); +} + +bool Query::bindCol(BindableStringBase &buffer) +{ + NOT_NULL(impl); + return impl->bindCol(buffer); +} + +bool Query::bindParameter(BindableStringBase &buffer) +{ + NOT_NULL(impl); + return impl->bindParameter(buffer); +} + +bool Query::bindCol(BindableUnicodeBase &buffer) +{ + NOT_NULL(impl); + return impl->bindCol(buffer); +} + +bool Query::bindParameter(BindableUnicodeBase &buffer) +{ + NOT_NULL(impl); + return impl->bindParameter(buffer); +} + +bool Query::bindCol(BindableBool &buffer) +{ + NOT_NULL(impl); + return impl->bindCol(buffer); +} + +bool Query::bindParameter(BindableBool &buffer) +{ + NOT_NULL(impl); + return impl->bindParameter(buffer); +} + +bool Query::bindParameter(BindableVarray &buffer) +{ + NOT_NULL(impl); + return impl->bindParameter(buffer); +} + +// ---------------------------------------------------------------------- + +/** + * Get the protocol being used to communicate with the database. + * + * @returns The protocol being used, or PROTOCOL_DEFAULT if the query is not + * associated with a database connection. + */ +Protocol Query::getProtocol() const +{ + if (inUse) + { + NOT_NULL(impl); + return impl->getProtocol(); + } + else + return PROTOCOL_DEFAULT; +} + +// ---------------------------------------------------------------------- + +/** + * Override this function to change the execution mode for the query. + * + * This function gets invoked after the QueryImpl has been created, so + * it can return different values depending on the protocol used. + * + * The default implementation returns MODE_SQL, which is suitable for + * most purposes. + */ +Query::QueryMode Query::getExecutionMode() const +{ + return MODE_SQL; +} + +// ---------------------------------------------------------------------- + +/** Override this function to return the debugging name for the query. + * The non-overriden version calls getSQL() and uses the result as the query name. + * Not const because getSQL() isn't const. + */ + +void Query::getDebugName(std::string &debugName) +{ + getSQL(debugName); +} + +// ---------------------------------------------------------------------- + +std::string Query::outputDataValues() const +{ + return impl->outputDataValues(); +} + +// ---------------------------------------------------------------------- + +void DBQueryNamespace::logLargeText(std::string const & logSection, std::string const & text) +{ + int const targetLineSize = 80; + int const maxLineSize = 100; + char buffer[maxLineSize+1]; + buffer[sizeof(buffer)-1]='\0'; + + int linePos = 0; + for (std::string::const_iterator sourcePos = text.begin(); sourcePos != text.end(); ++sourcePos) + { + if (*sourcePos < 27) + buffer[linePos++]='*'; // Hide unprintable/garbage/control characters + else + buffer[linePos++]=*sourcePos; + if ((linePos >= targetLineSize && *sourcePos == ' ') || (linePos >= maxLineSize)) + { + buffer[linePos]='\0'; + LOG(logSection.c_str(),(buffer)); + linePos=0; + } + } + if (linePos != 0) + { + buffer[linePos]='\0'; + LOG(logSection.c_str(),(buffer)); + } +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbQuery.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbQuery.h new file mode 100644 index 00000000..e21e24c4 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbQuery.h @@ -0,0 +1,157 @@ +/* DBQuery.h + * + * Definition of the base query class. Override this class to make + * queries for specific purposes. + * + * ODBC version + */ + +#ifndef _DBQUERY_H +#define _DBQUERY_H + +//#include "sharedDatabaseInterface/SQLC_Defs.h" +#include "sharedDatabaseInterface/DbQueryImplementation.h" //TODO: Do we want to include this? +#include "sharedDatabaseInterface/DbProtocol.def" + +namespace DB { + + class Session; + class QueryImpl; + class BindableLong; + class BindableVarray; + + class Query + { + public: + enum QueryMode {MODE_SQL, MODE_DML, MODE_PROCEXEC, MODE_PLSQL_REFCURSOR}; + + private: + QueryImpl *impl; + + Session *m_session; + bool inUse; + int m_execTime; + int m_fetchTime; + int m_execCount; + int m_fetchCount; + int m_rowsFetched; + std::string *m_debugName; + +/** + * Flag for the type of statement the query will run. + * + * Depending on the mode of operation, the query may need to be executed differently. + * In particular, some types of queries do not need to call bindColumns(), or may need to + * call it at different times. + * + * MODE_SQL is a query that may return rows. (The system allows DML statements to be sent using MODE_SQL, provided + * they bind no columns and never invoke fetch().) + * MODE_DML is a statement that updates rows. It will not return rows. + * MODE_PROCEXEC is a statement that invokes a stored procedure. It will not return rows, but it may alter + * parameters bound with bindParameter(). + * MODE_PLSQL_REFCURSOR is Oracle-specific. + */ + QueryMode m_queryMode; + bool m_useProcFetch; + +/** Because database results are bound to the memory location of members + of Query, copying them is not allowed. Do not define this function. +*/ + Query(const Query &rhs); + +/** Because database results are bound to the memory location of members + of Query, copying them is not allowed. Do not define this function. +*/ + Query &operator=(const Query &rhs); + + protected: +/** Switches to array mode to return an array of results, rather than a single row. + * Call this first, then call the bindCol functions with the first element in the array. + */ + void setColArrayMode(size_t skipSize, size_t numElements); + + bool bindCol(BindableLong &buffer); + bool bindParameter(BindableLong &buffer); + bool bindCol(BindableDouble &buffer); + bool bindParameter(BindableDouble &buffer); + bool bindCol(BindableStringBase &buffer); + bool bindParameter(BindableStringBase &buffer); + bool bindCol(BindableUnicodeBase &buffer); + bool bindParameter(BindableUnicodeBase &buffer); + bool bindCol(BindableBool &buffer); + bool bindParameter(BindableBool &buffer); + bool bindParameter(BindableVarray &buffer); + + Protocol getProtocol() const; + + public: + // functions to use as-is: + Query(); + virtual ~Query(); + + int fetch(); + int getNumRowsFetched() const; + void done(); + + int rowCount(); + + QueryMode getMode() const; + void setMode(QueryMode newMode); + + std::string outputDataValues() const; + + // functions to override: +/** Override this function to return the SQL command for the query. + */ + virtual void getSQL(std::string &sql)=0; +/** Override this function to bind the results returned by the query to + variables or to bind parameters in the query. + + See the Database Library documentation for more information, or refer to + the various Bindable types. An implementation of bind will probably + include lines like + +
+    mydata.BindCol(1,this);
+    myparameter.BindParameter(1,this);
+    
+*/ + virtual bool bindParameters()=0; + virtual bool bindColumns()=0; + + virtual QueryMode getExecutionMode() const; + + virtual void getDebugName(std::string &debugName); + + bool setup(Session *session); + bool prepare(); + bool exec(); + }; + +// ---------------------------------------------------------------------- + + inline Query::QueryMode Query::getMode() const + { + return m_queryMode; + } + +// ---------------------------------------------------------------------- + + inline void Query::setMode(Query::QueryMode newMode) + { + m_queryMode=newMode; + } + +// ---------------------------------------------------------------------- + + inline int Query::getNumRowsFetched() const + { + return m_rowsFetched; + } + +// ---------------------------------------------------------------------- +} //namespace + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbQueryImplementation.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbQueryImplementation.h new file mode 100644 index 00000000..a978dd77 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbQueryImplementation.h @@ -0,0 +1,89 @@ +// ====================================================================== +// +// DBQueryImpl.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DBQueryImpl_H +#define INCLUDED_DBQueryImpl_H + +// ====================================================================== + +#include "sharedDatabaseInterface/DbProtocol.def" + +// ====================================================================== + +namespace DB +{ + class BindableBool; + class BindableDouble; + class BindableInt64; + class BindableLong; + class BindableStringBase; + class BindableUnicodeBase; + class BindableVarray; + class Query; + class Session; + + /** + * A class to contain the database-specific implementation of a + * query. + * + * The constructor of Query creates an instance of QueryImpl. This + * allows the library user to derive classes from Query without + * knowing specifics of the implementation. + */ + class QueryImpl + { + protected: + Query *m_query; + + public: + explicit QueryImpl(Query *query); + virtual ~QueryImpl() {}; //lint !e1540 //We don't delete m_query (because we don't own it) + + virtual bool setup(Session *session) =0; + virtual bool prepare() =0; + virtual int fetch() =0; + virtual void done() =0; + virtual int rowCount() =0; //TODO: Should be getRowCount() to be more consistent + virtual bool exec() =0; + + virtual Protocol getProtocol() const =0; + + virtual void setColArrayMode(size_t skipSize, size_t numElements)=0; + + virtual bool bindCol(BindableLong &buffer) =0; + virtual bool bindParameter(BindableLong &buffer) =0; + virtual bool bindCol(BindableDouble &buffer) =0; + virtual bool bindParameter(BindableDouble &buffer) =0; + virtual bool bindCol(BindableStringBase &buffer) =0; + virtual bool bindParameter(BindableStringBase &buffer) =0; + virtual bool bindCol(BindableUnicodeBase &buffer) =0; + virtual bool bindParameter(BindableUnicodeBase &buffer) =0; + virtual bool bindCol(BindableBool &buffer) =0; + virtual bool bindParameter(BindableBool &buffer) =0; + virtual bool bindParameter(BindableVarray &buffer) =0; + + virtual std::string outputDataValues() const =0; + + private: + QueryImpl(); // disable + QueryImpl(const QueryImpl &); // disable + QueryImpl & operator=(const QueryImpl &); // disable + }; + +} + +// ====================================================================== + +inline DB::QueryImpl::QueryImpl(DB::Query *query) : + m_query(query) +{ +} + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbRow.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbRow.cpp new file mode 100644 index 00000000..457a81fc --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbRow.cpp @@ -0,0 +1,43 @@ +// ====================================================================== +// +// DBRow.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbRow.h" + +volatile int DB::Row::m_sRowsCreated = 0; +volatile int DB::Row::m_sRowsDeleted = 0; + + +// ---------------------------------------------------------------------- + +DB::Row::Row() +{ + ++m_sRowsCreated; +} + +// ---------------------------------------------------------------------- +DB::Row::Row( const Row& ) +{ + ++m_sRowsCreated; +} + +// ---------------------------------------------------------------------- + +DB::Row::~Row() +{ + ++m_sRowsDeleted; +} + +// ---------------------------------------------------------------------- + +void DB::Row::copy(const Row &rhs) +{ + UNREF(rhs); + FATAL(true,("Row::copy() invoked. This error probably means that copy() was not implemented for a class derived from Row.")); +} +// ====================================================================== + diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbRow.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbRow.h new file mode 100644 index 00000000..65271850 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbRow.h @@ -0,0 +1,58 @@ +// ====================================================================== +// +// DBRow.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DBRow_H +#define INCLUDED_DBRow_H + +// ====================================================================== + +namespace DB +{ + +/** + * This is the base class for classes that represent a row of data returned + * from the database. + * + * The goal is that in many cases, classes derived from Row will be generated + * directly from the database schema. Therefore, the class is kept as simple + * as possible. + */ +struct Row +{ + Row(); + Row( const Row& ); + + virtual ~Row(); + /** + * This function is like a virtual operator=. + * + * Implement this function in derived classes. It should do something + * equivalent to *this = dynamic_cast(rhs) + * + * This function is not pure virtual so that derived classes do not need + * to define it if they do not plan to use it. However, the default + * implementation will FATAL. + * + * @todo We could make a CopyableRow class derived from Row that has + * this function as pure virtual. However, that might limit our ability + * to generate Row's directly from the database schema without regards to + * how they will be used. + */ + virtual void copy(const Row &rhs); + + static int getRowsCreated() { return m_sRowsCreated; } + static int getRowsDeleted() { return m_sRowsDeleted; } + +private: + static volatile int m_sRowsCreated; + static volatile int m_sRowsDeleted; +}; + +} // namespace + +// ====================================================================== +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbServer.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbServer.cpp new file mode 100644 index 00000000..13f7460e --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbServer.cpp @@ -0,0 +1,410 @@ +// ====================================================================== +// +// DBServer.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbServer.h" + +#include +#include "sharedDatabaseInterface/DbSession.h" +#include "sharedDatabaseInterface/DbProfiler.h" +#include "sharedFoundation/ExitChain.h" +#include "sharedFoundation/Os.h" +#include "sharedLog/Log.h" + +#ifdef DBLIBRARY_ODBC +#include "OdbcServer.h" //TODO: is this the best way to do DBServer::create() +#endif +#ifdef DBLIBRARY_OCI +#include "OciServer.h" +#endif + +using namespace DB; + +// ====================================================================== + +Mutex Server::ms_profileMutex; +bool Server::ms_enableProfiling = false; +bool Server::ms_enableVerboseMode = false; +DB::Profiler *Server::ms_profiler = NULL; +int Server::ms_reconnectTime=0; +int Server::ms_maxErrorCountBeforeDisconnect=5; +int Server::ms_maxErrorCountBeforeBailout=10; +int Server::ms_errorSleepTime=5000; +int Server::ms_disconnectSleepTime=45000; +int Server::ms_execCount=0; +int Server::ms_fetchCount=0; +int Server::ms_prefetchNumRows=0; +int Server::ms_prefetchMemory=0; +bool Server::ms_fatalOnError=false; +bool Server::ms_fatalOnDataError=false; + +// ====================================================================== + +Server::SesListElem::SesListElem (Session *_ses, SesListElem *_next) : ses (_ses), next (_next) +{ +} + +// ---------------------------------------------------------------------- + +Server::Server(const char *_dsn, const char *_uid, const char *_pwd, bool useMemoryManager) : + sessionList (NULL), + sessionListLock(), + m_useMemoryManager(useMemoryManager) +{ + strncpy(dsn,_dsn,sizeof(dsn)-1); + strncpy(uid,_uid,sizeof(uid)-1); + strncpy(pwd,_pwd,sizeof(pwd)-1); + + //insure terminating \0: + dsn[sizeof(dsn)-1]='\0'; + uid[sizeof(uid)-1]='\0'; + pwd[sizeof(pwd)-1]='\0'; +} + +// ---------------------------------------------------------------------- + +// Server destructor +// TODO: what to do if deallocating handles failes? (This could happen if connection isn't closed properly, etc.) +Server::~Server() +{ +} + +// ---------------------------------------------------------------------- + +/** + * Establish connections to the database server. + * Sessions will be pre-connected and put in the pool of sessions. The + * caller can later request up to that number of sessions by calling + * GetSession(), with no blocking. Requests for additional sessions may + * block. + * + * @param numSessions The number of sessions to establish. + */ + +bool Server::connect(int numSessions) +{ + disconnect(); + + for (int i=0;iconnect()) + { + ses->disconnect(); + LOG("DatabaseError", ("Failed to connect to database in getSession()")); + FATAL(true, ("Failed to connect to database in getSession()")); + } + pushSession (ses); + } + + return true; +} + +// ---------------------------------------------------------------------- + +/** + * Drop all the sessions in the pool of sessions. + * After calling Disconnect(), any call to GetSession() will require + * establishing a new connection to the database server. + * Note that this call only affects Sessions in the pool. Sessions that + * are currently in use are not affected. + */ +bool Server::disconnect() +{ + Session *ses=popSession(); + while(ses) + { + delete ses; + ses=popSession(); + } + return true; +} + +// ---------------------------------------------------------------------- + +/** + * Get a Session object, representing a session on the database server. + * Will return a Session object from the pool, if one is avaiable. + * Otherwise, it will establish a new session. Warning: establishing + * a new session may require a database roundtrip. (i.e. It may block.) + * To avoid blocking, use connect() ahead of time to establish all the + * needed Sessions. + */ +Session *Server::getSession() +{ + Session *ses=popSession(); + + if (!ses) // pool was empty, so make a new one. TODO: set a limit on max # of sessions + { + ses=createSession(); + while (!ses->connect()) + { + ses->disconnect(); + LOG("DatabaseError", ("Failed to connect to database in getSession(). Will reconnect in 30 seconds.")); + Os::sleep(30000); + } + } + return ses; +} + +// ---------------------------------------------------------------------- + +/** + * Call when finished with a Session object. + * Returns the Session object to the pool. + * @todo for debugging, make sure the session released actually came from this server + */ +void Server::releaseSession(Session *session) +{ + session->reset(); + pushSession(session); +} + +// ---------------------------------------------------------------------- + +void Server::pushSession(Session *session) +{ + sessionListLock.enter(); + + SesListElem *e=new SesListElem(session, sessionList); + sessionList=e; + + sessionListLock.leave(); +} + +// ---------------------------------------------------------------------- + +Session *Server::popSession() +{ + sessionListLock.enter(); + + SesListElem *e=sessionList; + if (e) + { + Session *ses=e->ses; + sessionList=e->next; + delete e; + sessionListLock.leave(); + return ses; + } + else + { + sessionListLock.leave(); + return NULL; + } +} + +// ---------------------------------------------------------------------- + +Server *Server::create(const char *_dsn, const char *_uid, const char *_pwd, Protocol protocol, bool useMemoryManager) +{ + UNREF(_dsn); + UNREF(_uid); + UNREF(_pwd); + + switch (protocol) + { + +#ifdef DBLIBRARY_ODBC + case PROTOCOL_ODBC: + return new ODBCServer(_dsn,_uid,_pwd); +#endif + +#ifdef DBLIBRARY_OCI + case PROTOCOL_OCI: + return new OCIServer(_dsn,_uid,_pwd, useMemoryManager); +#endif + + case PROTOCOL_DEFAULT: + // Create any kind of server we can, prefering ODBCServer +#ifdef DBLIBRARY_ODBC + return new ODBCServer(_dsn,_uid,_pwd); +#endif + +#ifdef DBLIBRARY_OCI + return new OCIServer(_dsn,_uid,_pwd, useMemoryManager); +#endif + + default: + FATAL(true,("Database protocol specified in Server::create() is not available in this build of the library.\n")); + return 0; + } +} + +// ---------------------------------------------------------------------- + +Protocol Server::getProtocolByName(const std::string &name) +{ + +#ifdef DBLIBRARY_ODBC + if (name=="ODBC") + return PROTOCOL_ODBC; +#endif + +#ifdef DBLIBRARY_OCI + if (name=="OCI") + return PROTOCOL_OCI; +#endif + + if (name=="DEFAULT") + return PROTOCOL_DEFAULT; + + FATAL(true,("Specified protocol (%s) is not available in this version of the database library.",name.c_str())); + return PROTOCOL_DEFAULT; +} + +// ---------------------------------------------------------------------- + +void Server::addQueryProfileData(const std::string &queryName, int execTime, int fetchTime, int execCount, int fetchCount) +{ + ms_profileMutex.enter(); + + DEBUG_FATAL(!isProfilingEnabled(),("Programmer bug: you're supposed to check whether profiling is enabled before calling DB::Server::addProfileData (or there could be a race condition that is disabling profiling while queries are running).\n")); + if (!ms_profiler) + { + ms_profiler = new DB::Profiler; + } + + ms_profiler->addQueryProfileData(queryName,execTime,fetchTime,execCount,fetchCount); + ms_profileMutex.leave(); +} + +// ---------------------------------------------------------------------- + +void Server::debugOutputProfile() +{ + ms_profileMutex.enter(); + if (ms_profiler) + ms_profiler->debugOutput(); + else + DEBUG_REPORT_LOG(true,("No database profiler.\n")); + ms_profileMutex.leave(); +} + +// ---------------------------------------------------------------------- + +/** + * Stop collecting profile data and delete the Profiler. + */ +void Server::endProfiling() +{ + ms_profileMutex.enter(); + delete ms_profiler; + ms_profiler = NULL; + ms_enableProfiling = false; + ms_profileMutex.leave(); +} + +// ---------------------------------------------------------------------- + +/** + * Set a timeout for server connections. If a connection to the database + * has been open for more than reconnectTime, it will drop the connection + * and reestablish it the next time Session::reset() is called. + */ +void Server::setReconnectTime(int reconnectTime) +{ + ms_reconnectTime = reconnectTime; +} + +// ---------------------------------------------------------------------- + +/** + * Count the total number of queries executed and the total number of fetches + * for profiling. (This level of profiling is always enabled. The + * per-query profiling is optionally enabled.) + */ +void Server::addQueryCount(int execCount, int fetchCount) +{ + ms_profileMutex.enter(); + + ms_execCount+=execCount; + ms_fetchCount+=fetchCount; + + ms_profileMutex.leave(); +} + +// ---------------------------------------------------------------------- + +void Server::enablePrefetch(int numRows, int memory) +{ + ms_prefetchNumRows = numRows; + ms_prefetchMemory = memory; +} + +// ---------------------------------------------------------------------- + +void Server::disablePrefetch() +{ + ms_prefetchNumRows = 0; +} + +// ---------------------------------------------------------------------- + +bool Server::isPrefetchEnabled() +{ + return (ms_prefetchNumRows > 0); +} + +// ---------------------------------------------------------------------- + +int Server::getPrefetchRows() +{ + return ms_prefetchNumRows; +} + +// ---------------------------------------------------------------------- + +int Server::getPrefetchMemory() +{ + return ms_prefetchMemory; +} + +// ---------------------------------------------------------------------- + +void Server::getAndResetQueryCount(int &execCount, int &fetchCount) +{ + ms_profileMutex.enter(); + + execCount=ms_execCount; + fetchCount=ms_fetchCount; + + ms_execCount=0; + ms_fetchCount=0; + + ms_profileMutex.leave(); +} + +// ---------------------------------------------------------------------- + +void Server::setFatalOnError(bool fatalOnError) +{ + ms_fatalOnError=fatalOnError; +} + +// ---------------------------------------------------------------------- + +bool Server::getFatalOnError() +{ + return ms_fatalOnError; +} + +// ---------------------------------------------------------------------- + +void Server::setFatalOnDataError(bool fatalOnDataError) +{ + ms_fatalOnDataError=fatalOnDataError; +} + +// ---------------------------------------------------------------------- + +bool Server::getFatalOnDataError() +{ + return ms_fatalOnDataError; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbServer.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbServer.h new file mode 100644 index 00000000..16edb79c --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbServer.h @@ -0,0 +1,274 @@ +// ====================================================================== +// +// DBServer.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DBServer_H +#define INCLUDED_DBServer_H + +// ====================================================================== + +#include "sharedDatabaseInterface/DbProtocol.def" +#include "sharedSynchronization/Mutex.h" + +// ====================================================================== + +namespace DB { + + class Session; + class Profiler; + +/** + * The Server class handles connecting to an external database server. It knows how to establish connections, + * do error handling, etc. Create a Server object for each database you with to connect to. Then, call getSession() + * to get a Session object, which represents an individual session on the database server. Queries can be passed + * to the Session object. + * + * The Server class does session pooling. Call the Connect() method to pre-allocate some Sessions in a pool. + * getSession() will return one of the Sessions from the pool, without blocking. If the pool is empty, getSession() + * will establish a new Session, which may require some blocking while the underlying network connection is set up. + * + * The Server class is threadsafe. Servers can be shared between threads. Sessions and Queries should not be shared. + * @todo Actually, it isn't threadsafe. Need to fix that. + */ + class Server + { +/** A sub-class to hold the elements in the list of Sessions making up the Session pool + */ + struct SesListElem { + Session *ses; + SesListElem *next; + + SesListElem (Session *_ses, SesListElem *_next); + }; + + protected: + + char dsn[80],uid[80],pwd[80]; + + private: + +/** Pool of available Sessions. + */ + SesListElem *sessionList; + + Mutex sessionListLock; + bool m_useMemoryManager; + + static Mutex ms_profileMutex; + static bool ms_enableProfiling; + static bool ms_enableVerboseMode; + static Profiler *ms_profiler; + + static int ms_prefetchNumRows; + static int ms_prefetchMemory; + + static int ms_reconnectTime; + static int ms_maxErrorCountBeforeDisconnect; + static int ms_maxErrorCountBeforeBailout; + static int ms_errorSleepTime; + static int ms_disconnectSleepTime; + + static int ms_execCount; + static int ms_fetchCount; + + static bool ms_fatalOnError; + static bool ms_fatalOnDataError; + + public: + + Server(const char *_dsn, const char *_uid, const char *_pwd, bool useMemoryManager); // TODO: should be protected? + virtual ~Server(); + + static Server *create(const char *_dsn, const char *_uid, const char *_pwd, Protocol protocol, bool useMemoryManager); + static Protocol getProtocolByName(const std::string &name); + + Session *getSession(); + void releaseSession(Session *session); + + bool connect(int numSessions=1); + bool disconnect(); + + const char *getDSN() const; + bool getUseMemoryManager() const; + + static void enableProfiling(); + static void disableProfiling(); + static void endProfiling(); + static bool isProfilingEnabled(); + static void enableVerboseMode(); + static void disableVerboseMode(); + static bool isVerboseModeEnabled(); + static void enablePrefetch(int numRows, int memory); + static void disablePrefetch(); + static bool isPrefetchEnabled(); + static int getPrefetchRows(); + static int getPrefetchMemory(); + static void addQueryProfileData(const std::string &queryName, int execTime, int fetchTime, int execCount, int fetchCount); + static void debugOutputProfile(); + + static void addQueryCount(int execCount, int fetchCount); + static void getAndResetQueryCount(int &execCount, int &fetchCount); + + static void setReconnectTime(int reconnectTime); + static int getReconnectTime(); + + static void setMaxErrorCountBeforeDisconnect(int maxErrorCountBeforeDisconnect); + static int getMaxErrorCountBeforeDisconnect(); + + static void setMaxErrorCountBeforeBailout(int maxErrorCountBeforeBailout); + static int getMaxErrorCountBeforeBailout(); + + static void setErrorSleepTime(int errorSleepTime); + static int getErrorSleepTime(); + + static void setDisconnectSleepTime(int disconnectSleepTime); + static int getDisconnectSleepTime(); + + static void setFatalOnError(bool fatalOnError); + static bool getFatalOnError(); + + static void setFatalOnDataError(bool fatalOnDataError); + static bool getFatalOnDataError(); + + protected: + + virtual Session *createSession() =0; + + private: + + void pushSession(Session *session); + Session *popSession(); + + private: + Server(); //disable + Server(const Server &); //disable + Server & operator=(const Server &); //disable + }; //lint !e1712 // no default constructor for SesListElem + +} + +// ====================================================================== + +inline const char *DB::Server::getDSN() const +{ + return dsn; +} + +// ---------------------------------------------------------------------- + +inline bool DB::Server::isProfilingEnabled() +{ + return ms_enableProfiling; +} + +// ---------------------------------------------------------------------- + +inline bool DB::Server::isVerboseModeEnabled() +{ + return ms_enableVerboseMode; +} + +// ---------------------------------------------------------------------- + +inline void DB::Server::enableProfiling() +{ + ms_enableProfiling = true; +} + +// ---------------------------------------------------------------------- + +inline void DB::Server::disableProfiling() +{ + ms_enableProfiling = false; +} + +// ---------------------------------------------------------------------- + +inline void DB::Server::enableVerboseMode() +{ + ms_enableVerboseMode = true; +} + +// ---------------------------------------------------------------------- + +inline void DB::Server::disableVerboseMode() +{ + ms_enableVerboseMode = false; +} + +// ---------------------------------------------------------------------- + +inline int DB::Server::getReconnectTime() +{ + return ms_reconnectTime; +} + +// ---------------------------------------------------------------------- + +inline bool DB::Server::getUseMemoryManager() const +{ + return m_useMemoryManager; +} + +// ---------------------------------------------------------------------- + +inline void DB::Server::setMaxErrorCountBeforeDisconnect(int maxErrorCountBeforeDisconnect) +{ + ms_maxErrorCountBeforeDisconnect = maxErrorCountBeforeDisconnect; +} + +// ---------------------------------------------------------------------- + +inline int DB::Server::getMaxErrorCountBeforeDisconnect() +{ + return ms_maxErrorCountBeforeDisconnect; +} + +// ---------------------------------------------------------------------- + +inline void DB::Server::setMaxErrorCountBeforeBailout(int maxErrorCountBeforeBailout) +{ + ms_maxErrorCountBeforeBailout = maxErrorCountBeforeBailout; +} + +// ---------------------------------------------------------------------- + +inline int DB::Server::getMaxErrorCountBeforeBailout() +{ + return ms_maxErrorCountBeforeBailout; +} + +// ---------------------------------------------------------------------- + +inline void DB::Server::setErrorSleepTime(int errorSleepTime) +{ + ms_errorSleepTime=errorSleepTime; +} + +// ---------------------------------------------------------------------- + +inline int DB::Server::getErrorSleepTime() +{ + return ms_errorSleepTime; +} + +// ---------------------------------------------------------------------- + +inline void DB::Server::setDisconnectSleepTime(int disconnectSleepTime) +{ + ms_disconnectSleepTime=disconnectSleepTime; +} + +// ---------------------------------------------------------------------- + +inline int DB::Server::getDisconnectSleepTime() +{ + return ms_disconnectSleepTime; +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbSession.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbSession.cpp new file mode 100644 index 00000000..9f5c35aa --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbSession.cpp @@ -0,0 +1,128 @@ +// ====================================================================== +// +// DBSession.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbSession.h" +#include "sharedLog/Log.h" + +#include +#include +#include + +#include "sharedDatabaseInterface/DbQuery.h" +#include "sharedDatabaseInterface/DbServer.h" + +using namespace DB; + +// ====================================================================== + +/** + * Do not create session objects directly. + * Instead, use DB::Server::GetSession(). + */ +Session::Session() : inUse(false), connected(false), lastQuery(0), lastQueryStatement(), m_execCount(0), m_fetchCount(0), m_fatalOnError(false) +{ +} + +// ---------------------------------------------------------------------- + +Session::~Session() +{ + DEBUG_FATAL(inUse,("Deleted a session that was still in use.\n")); + DEBUG_FATAL(connected,("Derived session did not disconnect.")); +} //lint !e1540 // didn't delete lastQuery + +// ---------------------------------------------------------------------- + +/** + * Execute a database query, represented by a Query object. + */ +bool Session::exec(Query *query) +{ + NOT_NULL(query); + + if (!inUse) // if the query was just run, we don't need to do the setup again + { + if (!query->setup(this)) return false; + } + else + { + // debugging: make sure this is the _same_ query + DEBUG_FATAL(lastQuery!=query,("Called Session::exec() on a query without calling done() on the previous query.")); + } + + if (!query->exec()) + { + std::string context; + query->getDebugName(context); + WARNING(true, ("Context from previous database error: %s", context.c_str())); + LOG("DatabaseError", ("Context from previous database error: %s", context.c_str())); + return false; + } + + lastQuery=query; + inUse=true; + return true; +} + +// ---------------------------------------------------------------------- + +/** [Function used by other classes in the database library.] Called when a Query object finishes its work, so that + the Session is now free to run another Query. + */ +void Session::queryDone() +{ + inUse=false; + lastQuery=0; +} + +// ---------------------------------------------------------------------- + +void Session::addQueryCount(int execCount, int fetchCount) +{ + m_execCount += execCount; + m_fetchCount += fetchCount; +} + +// ---------------------------------------------------------------------- + +void Session::updateServerQueryCount() +{ + Server::addQueryCount(m_execCount, m_fetchCount); + m_execCount=0; + m_fetchCount=0; +} + +// ---------------------------------------------------------------------- + +void Session::setLastQueryStatement(std::string const & queryStatement) +{ + lastQueryStatement = queryStatement; +} + +// ---------------------------------------------------------------------- + +std::string Session::getLastQueryStatement() const +{ + return lastQueryStatement; +} + +// ---------------------------------------------------------------------- + +void Session::setFatalOnError(bool fatalOnError) +{ + m_fatalOnError=fatalOnError; +} + +// ---------------------------------------------------------------------- + +bool Session::getFatalOnError() const +{ + return m_fatalOnError; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbSession.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbSession.h new file mode 100644 index 00000000..4e2e4571 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/DbSession.h @@ -0,0 +1,141 @@ +// ====================================================================== +// +// DBSession.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DBSession_H +#define INCLUDED_DBSession_H + +// ====================================================================== + +#include + +// ====================================================================== + +namespace DB { + +class Server; +class Query; +class QueryImpl; + +/** + * A DBSession is an object representing a connection on which a series + * of queries can be performed. A database session is much like a session + * at a command prompt in an operating system. Within a session, one query + * can be sent at a time and nothing else can be done in that session until + * the results of the query are returned. A session may be thought of as a + * logical connection to the database. + * + * Do not create DBSession objects directly. Use the GetSession method on + * DBServer instead. Be sure to call DBServer::ReleaseSession() when you + * are done with the session, so that others may use it. + * + * Note that DBSession objects can't be copied. Database libraries do not + * allow a session to be cloned, so the semantics of copying a session object + * are not clear. Therefore, it is not allowed. + * + * TODO: Consider making an autoptr-like object to hold sessions. It would + * automatically call ReleaseSession() when it goes out of scope. + */ +class Session +{ + bool inUse; + + // following are disallowed: + Session(const Session& rhs); + Session &operator=(const Session& rhs); + +protected: + bool connected; + +private: + Query *lastQuery; + std::string lastQueryStatement; + int m_execCount; + int m_fetchCount; + bool m_fatalOnError; + +public: + Session(); // TODO: should be private + virtual ~Session(); + + // functions used by other SQLClasses + void queryDone(); + +/** [Function used by other classes in the database library.] Establish connection to a database server. + */ + virtual bool connect() =0; + +/** [Function used by other classes in the database library.] Drop connection to a database server. + */ + virtual bool disconnect() =0; + +/** [Function used by other classes in the database library.] Reset the session to its default status + (e.g. before returning it to the session pool). Undo any special options the user may have set, + such as setting manual-commit mode. + + If this returns false, an error occurred trying to restore the state. The best thing to do is + probably to drop the session and establish a new one. + */ + virtual bool reset() =0; + + bool exec(Query *query); + +/** Set the session to auto-commit or manual-commit mode. + + If in auto-commit mode, every query set to Exec() is automatically + commited immediately. If in manual commit mode, the changes made + by queries passed to Exec() are considered to be part of a transaction. + They are remembered by the database, but they don't take effect + immediately. When CommitTransaction() is called, the changes all + take effect simultaneously. + + (Warning: switching from manual-commit mode to auto-commit mode + automatically commits any open transactions.) + + @param autocommit TRUE turns on auto-commit mode (the default), + FALSE turns on manual-commit mode + */ + virtual bool setAutoCommitMode(bool autocommit) =0; + +/** Set the session to FATAL on a database error, rather than returning + "false" to the caller. + */ + void setFatalOnError(bool fatalOnError); + bool getFatalOnError() const; + +/** Commit a transaction. + Everything done within the transaction will simultaneously take effect. + */ + virtual bool commitTransaction() =0; + +/** Rollback a transaction + Evertyhing done within the transaction will be undone. + */ + virtual bool rollbackTransaction() =0; + +/** Add to the count of the number of fetches and executes. + + Each query adds to this count as it finishes. + */ + void addQueryCount(int execCount, int fetchCount); + +/** Update the total count of fetches and executes in the Server object + */ + void updateServerQueryCount(); + +/** Set/Get the last query statement that was run; useful for debugging purposes + */ + void setLastQueryStatement(std::string const & queryStatement); + std::string getLastQueryStatement() const; + + virtual QueryImpl *createQueryImpl(Query *owner) const =0; +}; + +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/FirstSharedDatabaseInterface.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/FirstSharedDatabaseInterface.h new file mode 100644 index 00000000..dacf606a --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/FirstSharedDatabaseInterface.h @@ -0,0 +1,20 @@ +// ====================================================================== +// +// FirstServerDatabaseInterface.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_FirstServerDatabaseInterface_H +#define INCLUDED_FirstServerDatabaseInterface_H + +// ====================================================================== + +#include "sharedFoundation/FirstSharedFoundation.h" +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedMemoryManager/FirstSharedMemoryManager.h" + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/NullEncodedStandardString.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/NullEncodedStandardString.cpp new file mode 100644 index 00000000..2b6ca457 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/NullEncodedStandardString.cpp @@ -0,0 +1,89 @@ +// ====================================================================== +// +// NullEncodedStandardString.cpp +// copyright (c) 2004 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/NullEncodedStandardString.h" + +#include "Archive/Archive.h" +#include "Archive/ByteStream.h" +#include "UnicodeUtils.h" +#include "unicodeArchive/UnicodeArchive.h" + +// ====================================================================== + +namespace NullEncodedStandardStringNamespace +{ + std::string s_nullString; + std::string s_encodedNull( " " ); // blank space +} +using namespace NullEncodedStandardStringNamespace; + +// ====================================================================== + +NullEncodedStandardString::NullEncodedStandardString(std::string const & src) : + m_value(src.empty() ? s_encodedNull : src) +{ +} + +// ---------------------------------------------------------------------- + +NullEncodedStandardString:: NullEncodedStandardString() : + m_value() +{ +} + +// ---------------------------------------------------------------------- + +void NullEncodedStandardString::setValue(std::string const & src) +{ + m_value = src.empty() ? s_encodedNull : src; +} + +// ---------------------------------------------------------------------- + +std::string const & NullEncodedStandardString::getValue() const +{ + if (m_value == s_encodedNull) + return s_nullString; + else + return m_value; +} + +// ---------------------------------------------------------------------- + +std::string const & NullEncodedStandardString::getValueEncoded() const +{ + return m_value; +} + +// ---------------------------------------------------------------------- + +void NullEncodedStandardString::setValueEncoded(std::string const & src) +{ + m_value = src; +} + +// ====================================================================== + +namespace Archive +{ + void get(ReadIterator & source, NullEncodedStandardString & target) + { + std::string temp; + get(source, temp); + target.setValue(temp); + } + + // ---------------------------------------------------------------------- + + void put(ByteStream & target, const NullEncodedStandardString & source) + { + put(target, source.getValue()); + } +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/NullEncodedStandardString.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/NullEncodedStandardString.h new file mode 100644 index 00000000..ff154c7c --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/NullEncodedStandardString.h @@ -0,0 +1,47 @@ +// ====================================================================== +// +// NullEncodedStandardString.h +// copyright (c) 2004 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_NullEncodedStandardString_H +#define INCLUDED_NullEncodedStandardString_H + +// ====================================================================== + +#include "Unicode.h" + +// ====================================================================== + +/** + * A unicode-string-compatible class that encodes a null value as a single space + */ +class NullEncodedStandardString +{ + public: + NullEncodedStandardString(); + explicit NullEncodedStandardString(std::string const & src); + std::string const & getValue() const; + void setValue(std::string const & src); + std::string const & getValueEncoded() const; + void setValueEncoded(std::string const & src); + + private: + std::string m_value; +}; + +// ====================================================================== + +namespace Archive +{ + class ReadIterator; + class ByteStream; + + void get(ReadIterator & source, NullEncodedStandardString & target); + void put(ByteStream & target, const NullEncodedStandardString & source); +} + +// ====================================================================== + +#endif // INCLUDED_NullEncodedStandardString_H diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/NullEncodedUnicodeString.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/core/NullEncodedUnicodeString.cpp new file mode 100644 index 00000000..ee58475d --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/NullEncodedUnicodeString.cpp @@ -0,0 +1,89 @@ +// ====================================================================== +// +// NullEncodedUnicodeString.cpp +// copyright (c) 2004 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/NullEncodedUnicodeString.h" + +#include "Archive/Archive.h" +#include "Archive/ByteStream.h" +#include "UnicodeUtils.h" +#include "unicodeArchive/UnicodeArchive.h" + +// ====================================================================== + +namespace NullEncodedUnicodeStringNamespace +{ + Unicode::String s_nullString; + Unicode::String s_encodedNull(Unicode::narrowToWide(" ")); +} +using namespace NullEncodedUnicodeStringNamespace; + +// ====================================================================== + +NullEncodedUnicodeString::NullEncodedUnicodeString(Unicode::String const & src) : + m_value(src.empty() ? s_encodedNull : src) +{ +} + +// ---------------------------------------------------------------------- + +NullEncodedUnicodeString:: NullEncodedUnicodeString() : + m_value() +{ +} + +// ---------------------------------------------------------------------- + +void NullEncodedUnicodeString::setValue(Unicode::String const & src) +{ + m_value = src.empty() ? s_encodedNull : src; +} + +// ---------------------------------------------------------------------- + +Unicode::String const & NullEncodedUnicodeString::getValue() const +{ + if (m_value == s_encodedNull) + return s_nullString; + else + return m_value; +} + +// ---------------------------------------------------------------------- + +Unicode::String const & NullEncodedUnicodeString::getValueEncoded() const +{ + return m_value; +} + +// ---------------------------------------------------------------------- + +void NullEncodedUnicodeString::setValueEncoded(Unicode::String const & src) +{ + m_value = src; +} + +// ====================================================================== + +namespace Archive +{ + void get(ReadIterator & source, NullEncodedUnicodeString & target) + { + Unicode::String temp; + get(source, temp); + target.setValue(temp); + } + + // ---------------------------------------------------------------------- + + void put(ByteStream & target, const NullEncodedUnicodeString & source) + { + put(target, source.getValue()); + } +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/core/NullEncodedUnicodeString.h b/engine/shared/library/sharedDatabaseInterface/src/shared/core/NullEncodedUnicodeString.h new file mode 100644 index 00000000..b28de588 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/core/NullEncodedUnicodeString.h @@ -0,0 +1,47 @@ +// ====================================================================== +// +// NullEncodedUnicodeString.h +// copyright (c) 2004 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_NullEncodedUnicodeString_H +#define INCLUDED_NullEncodedUnicodeString_H + +// ====================================================================== + +#include "Unicode.h" + +// ====================================================================== + +/** + * A unicode-string-compatible class that encodes a null value as a single space + */ +class NullEncodedUnicodeString +{ + public: + NullEncodedUnicodeString(); + explicit NullEncodedUnicodeString(Unicode::String const & src); + Unicode::String const & getValue() const; + void setValue(Unicode::String const & src); + Unicode::String const & getValueEncoded() const; + void setValueEncoded(Unicode::String const & src); + + private: + Unicode::String m_value; +}; + +// ====================================================================== + +namespace Archive +{ + class ReadIterator; + class ByteStream; + + void get(ReadIterator & source, NullEncodedUnicodeString & target); + void put(ByteStream & target, const NullEncodedUnicodeString & source); +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbLogEvent.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbLogEvent.cpp new file mode 100644 index 00000000..64e9144f --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbLogEvent.cpp @@ -0,0 +1,42 @@ +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbLogEvent.h" + +#include "sharedDatabaseInterface/DbQuery.h" +#include "sharedFoundation/Os.h" + +//#include "LogQueries.h" +//#include "Schema.h" + +//----------------------------------------------------------------------------- + +using namespace DB; + +//----------------------------------------------------------------------------- + +LogEvent::LogEvent() : + eventTime(Os::getRealSystemTime()) +{ +} + +//----------------------------------------------------------------------------- + +//bool TestEvent::save(DB::Session *session) +//{ +// DBQuery::SaveTestEvent qry; +// DBSchema::ServerLogRow data; + +// qry.setData(&data); +// qry.insertMode(); + +// data.event_time=eventTime; +// data.server=3434; +// data.event_id=1; + +// session->exec(&qry); + +// qry.done(); + +// return true; +//} + +//----------------------------------------------------------------------------- diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbLogEvent.h b/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbLogEvent.h new file mode 100644 index 00000000..bbe2debf --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbLogEvent.h @@ -0,0 +1,50 @@ +#ifndef _LOG_EVENT_H +#define _LOG_EVENT_H + +//----------------------------------------------------------------------------- + +namespace DB +{ + class Session; +} + +//----------------------------------------------------------------------------- + +namespace DB +{ + + /** + * This is an abstract base class for events that can be logged. + * + * To create a LogEvent for a particular purpose, derive from this class. + * Override the save function to do whatever is needed to record the + * particular event. + */ + class LogEvent + { + public: + /** + * Default constructor stored the current system time in eventTime. + */ + LogEvent(); + + public: + /** + * Saves the LogEvent to the database. + * + * Override this function in derived classes to save specific data. + */ + virtual bool save(DB::Session *session) =0; + + protected: + /** + * The time the event occurred, in seconds since the epoch. + */ + time_t eventTime; + }; + +} + +//----------------------------------------------------------------------------- + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbLogger.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbLogger.cpp new file mode 100644 index 00000000..63697978 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbLogger.cpp @@ -0,0 +1,107 @@ +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbLogger.h" + +#include "sharedDatabaseInterface/DbServer.h" +#include "sharedDatabaseInterface/DbTaskQueue.h" +#include "sharedDatabaseInterface/DbTaskSaveLog.h" + +//----------------------------------------------------------------------------- + +using namespace DB; + +//----------------------------------------------------------------------------- + +Logger::EventsType Logger::events; +int Logger::maxQueuedEvents; +real Logger::maxTimeBeforeSave; +real Logger::timeSinceLastSave=0; +TaskQueue *Logger::taskQueue=0; +bool Logger::installed=false; + +//----------------------------------------------------------------------------- +/** + * Set up the logger. + * + * + * @param server The database server to send events to. + * @param _maxQueuedEvents The maximum number of events that can be queued + * before a save is forced. + * @param _maxTimeBeforeSave The maximum amount of time that can pass before a + * save is forced. + */ + +void Logger::install(Server *server, int _maxQueuedEvents, real _maxTimeBeforeSave) +{ + maxQueuedEvents=_maxQueuedEvents; + maxTimeBeforeSave=_maxTimeBeforeSave; + //TODO: establish database connection + taskQueue=new TaskQueue(1,server,0); + + installed=true; +} + +//----------------------------------------------------------------------------- + +void Logger::remove() +{ + //TODO: wait for task queue to complete - or will destructor handle it? + delete taskQueue; +} + +//----------------------------------------------------------------------------- +/** + * Add an event to the queue to be logged. + * + * @param event The event to be logged. Will be deleted when the logger is done + * with it. + */ + +void Logger::log(LogEvent *event) +{ + events.push_back(event); +} + +//----------------------------------------------------------------------------- +/** + * Evaluates whether it is time to save the accumulated log events. + * + * This should be called periodically, i.e. once per game loop. + * + * @param time The time in seconds since this function was last invoked. + */ + +void Logger::update(real time) +{ + timeSinceLastSave+=time; + if ((timeSinceLastSave > 10) || (events.size() > 10)) + beginSave(); +} + +//----------------------------------------------------------------------------- +/** + * Start an asynchronous task to save the current events. + * + * We don't care if another save is already in progress, because save tasks + * should not conflict with eachother. + */ + +void Logger::beginSave() +{ + if (events.size() == 0) + return; + + TaskSaveLog *tsl=new TaskSaveLog; + + // Copy events to the new task + for (EventsType::iterator i=events.begin(); i!=events.end(); ++i) + { + tsl->addEvent(*i); + } + + events.clear(); + + taskQueue->asyncRequest(tsl); +} //lint !e429 // didn't free tsl + +//----------------------------------------------------------------------------- + diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbLogger.h b/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbLogger.h new file mode 100644 index 00000000..955bf7b4 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbLogger.h @@ -0,0 +1,64 @@ +#ifndef _LOGGER_H +#define _LOGGER_H + +//----------------------------------------------------------------------------- + +#include + +//----------------------------------------------------------------------------- + +namespace DB +{ + class LogEvent; + class TaskQueue; + class Server; +} + +//----------------------------------------------------------------------------- + +namespace DB +{ + + /** + * A class that organizes sending LogEvents to the database. + * + * This class will collect LogEvents and periodically send them to + * the database to be recorded. + */ + class Logger + { + public: + static void log(LogEvent *event); + // Maybe, in derived class: + // logPlayerEvent(int player, ...); + // etc. + + static void update(real time); + + public: + //TODO: come up with reasonable defaults for these parameters: + static void install(Server *server, int _maxQueuedEvents=10, real _maxTimeBeforeSave=10.0); + static void remove(void); + + private: + static void beginSave(void); + + private: + static bool installed; + + static int maxQueuedEvents; + static real maxTimeBeforeSave; + + typedef std::vector EventsType; + static EventsType events; + + static real timeSinceLastSave; + + static TaskQueue *taskQueue; + }; + +} + +//----------------------------------------------------------------------------- + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbTaskSaveLog.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbTaskSaveLog.cpp new file mode 100644 index 00000000..14d6c5b9 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbTaskSaveLog.cpp @@ -0,0 +1,47 @@ +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbTaskSaveLog.h" + +#include "sharedDatabaseInterface/DbLogEvent.h" + +//----------------------------------------------------------------------------- + +using namespace DB; + +//----------------------------------------------------------------------------- +/** + * Add a LogEvent to the list to be saved. + */ + +void TaskSaveLog::addEvent(LogEvent *event) +{ + events.push_back(event); +} + +//----------------------------------------------------------------------------- + +bool TaskSaveLog::process(DB::Session *session) +{ + for (EventsType::iterator i=events.begin(); i!=events.end(); ++i) + { + if(! ((*i)->save(session))) + return false; + } + return true; +} + +//----------------------------------------------------------------------------- + +void TaskSaveLog::onComplete() +{ + +} + +//----------------------------------------------------------------------------- + +TaskSaveLog::~TaskSaveLog() +{ + for (EventsType::iterator i=events.begin(); i!=events.end(); ++i) + delete (*i); +} + +//----------------------------------------------------------------------------- diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbTaskSaveLog.h b/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbTaskSaveLog.h new file mode 100644 index 00000000..2201c7ea --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/logger/DbTaskSaveLog.h @@ -0,0 +1,45 @@ +#ifndef _TASK_SAVE_LOG_H +#define _TASK_SAVE_LOG_H + +//----------------------------------------------------------------------------- + +#include + +#include "sharedDatabaseInterface/DbTaskRequest.h" + +//----------------------------------------------------------------------------- + +namespace DB +{ + class LogEvent; +} + +//----------------------------------------------------------------------------- + +namespace DB +{ + + /** + * An asynchronous task to save a set of LogEvents. + */ + class TaskSaveLog:public DB::TaskRequest + { + public: + void addEvent(LogEvent *event); + + public: + bool process(DB::Session *session); + void onComplete(); + + public: + virtual ~TaskSaveLog(); + + private: + typedef std::vector EventsType; + EventsType events; + }; + +} +//----------------------------------------------------------------------------- + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTaskQueue.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTaskQueue.cpp new file mode 100644 index 00000000..c95ec3b5 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTaskQueue.cpp @@ -0,0 +1,273 @@ +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbTaskQueue.h" + +#include "sharedDatabaseInterface/DbServer.h" +#include "sharedDatabaseInterface/DbTaskRequest.h" +#include "sharedDebug/PerformanceTimer.h" +#include "sharedFoundation/Os.h" +#include "sharedLog/Log.h" +#include "sharedThread/RunThread.h" + +#include + +using namespace DB; + +volatile int TaskQueue::sm_iTotalNumRequests = 0; +volatile int TaskQueue::sm_iTotalNumResults = 0; + + +bool TaskQueue::ms_workerThreadsLoggingEnabled = false; + +TaskQueue::TaskQueue(unsigned int _numThreads, Server *_server, int _threadGroupId) : + numThreads(_numThreads), + server(_server), + requestQ(), + requestQLock(), + requestQAdded(requestQLock), + resultQ(), + resultQLock(), + pauseLock(), + pauseSignal(pauseLock), + workers(), + shutdown(false), + paused(false), + updateCountLock(), + activeCount(_numThreads), + idleCount(0), + threadGroupId(_threadGroupId), + numRequests(0), + numResults(0), + threadIdLock(), + nextThreadId(0) +{ //lint !e1926 // using default constructors for several members -- is intentional + for (unsigned int i=0; i("DBTaskQueue", *this, &TaskQueue::workerThreadLoop); + ThreadHandle tempThreadHandle(threadObject); + workers.push_back(tempThreadHandle); + } +} + +TaskQueue::~TaskQueue() +{ + shutdown=true; + unpause(); + requestQAdded.signal(); // get the attention of waiting threads + for(unsigned int i=0; iwait(); + } + + while (!resultQ.empty()) + { + update(0); // empty out the result queue before quitting + } +} //lint !e1740 // didn't delete server + +void TaskQueue::asyncRequest(TaskRequest *req) +{ + requestQLock.enter(); + ++numRequests; + ++sm_iTotalNumRequests; + requestQ.push_front(req); + requestQAdded.signal(); + requestQLock.leave(); +} + +void TaskQueue::workerThreadLoop() +{ + threadIdLock.enter(); + int threadId=nextThreadId++; + threadIdLock.leave(); + Os::OsPID_t processId=Os::getProcessId(); + LOG("WorkerThreads",("Thread %d-%d (%d) starting",threadGroupId,threadId,processId)); + + Session * session = server->getSession(); + while (!shutdown) + { + TaskRequest *req; + + if (ms_workerThreadsLoggingEnabled) + LOG("WorkerThreads",("Thread %d-%d (%d) about to grab a task",threadGroupId,threadId, processId)); + requestQLock.enter(); + + updateCountLock.enter(); + --activeCount; + ++idleCount; + updateCountLock.leave(); + + while (requestQ.empty()) + { + if (shutdown) // only check for shutdown when there's nothing left to do + { + // .leave and wake next thread + requestQAdded.signal(); // on shutdown, each thread wakes up the next one + + updateCountLock.enter(); + --idleCount; + updateCountLock.leave(); + + requestQLock.leave(); + + server->releaseSession(session); + session=0; + + return; + } + + if (ms_workerThreadsLoggingEnabled) + LOG("WorkerThreads",("Thread %d-%d (%d) waiting for a task",threadGroupId,threadId,processId)); + requestQAdded.wait(); + } + + // locked + req=requestQ.back(); // FIFO: added to front, removed from back + requestQ.pop_back(); + --numRequests; + --sm_iTotalNumRequests; + if (!requestQ.empty()) + { + requestQAdded.signal(); + } + + updateCountLock.enter(); + --idleCount; + ++activeCount; + updateCountLock.leave(); + + requestQLock.leave(); + + // Check for the queue being paused + pauseLock.enter(); + while (paused) + { + updateCountLock.enter(); + ++idleCount; + --activeCount; + updateCountLock.leave(); + + pauseSignal.wait(); + + updateCountLock.enter(); + --idleCount; + ++activeCount; + updateCountLock.leave(); + } + pauseLock.leave(); + + NOT_NULL(session); + if (ms_workerThreadsLoggingEnabled) + LOG("WorkerThreads",("Thread %d-%d (%d) running",threadGroupId,threadId,processId)); + req->workerThreadLoop(session); + if (ms_workerThreadsLoggingEnabled) + LOG("WorkerThreads",("Thread %d-%d (%d) finished a loop (about to enter resultQLock)",threadGroupId,threadId,processId)); + resultQLock.enter(); + if (ms_workerThreadsLoggingEnabled) + LOG("WorkerThreads",("Thread %d-%d (%d) finished a loop (entered resultQLock)",threadGroupId,threadId,processId)); + ++numResults; + ++sm_iTotalNumResults; + if (ms_workerThreadsLoggingEnabled) + LOG("WorkerThreads",("Thread %d-%d (%d) finished a loop (incremented numResults)",threadGroupId,threadId,processId)); + resultQ.push_front(req); + if (ms_workerThreadsLoggingEnabled) + LOG("WorkerThreads",("Thread %d-%d (%d) finished a loop (did resultQ.push_front)",threadGroupId,threadId,processId)); + resultQLock.leave(); + if (ms_workerThreadsLoggingEnabled) + LOG("WorkerThreads",("Thread %d-%d (%d) finished a loop (left resultQLock)",threadGroupId,threadId,processId)); + } + + server->releaseSession(session); + session=0; +} + +void TaskQueue::update(float maxTime) +{ + PerformanceTimer timer; + timer.start(); + + TaskRequest *req; + + resultQLock.enter(); + + while (!resultQ.empty() && (maxTime==0 || timer.getSplitTime() < maxTime)) + { + req=resultQ.back(); + resultQ.pop_back(); + --numResults; + --sm_iTotalNumResults; + resultQLock.leave(); + + if (req->mainThreadFinish()) + { + req->mainThreadOnComplete(); + delete req; + } + else + { + // if Finish() returns false, more worker thread processing is needed + asyncRequest(req); + } + resultQLock.enter(); + } + resultQLock.leave(); +} + +void TaskQueue::cancel() +{ + requestQLock.enter(); + while(!requestQ.empty()) + { + TaskRequest *req=requestQ.back(); + delete req; + requestQ.pop_back(); + } + requestQLock.leave(); +} + +int TaskQueue::getNumPendingTasks() +{ + return numRequests; +} + +int TaskQueue::getNumFinishedTasks() +{ + return numResults; +} + +int TaskQueue::getNumActiveTasks() +{ + return activeCount; +} + +void TaskQueue::report() +{ + DEBUG_REPORT_LOG(true,("Task queue status: %i threads, %i active, %i idle, %i undelegated tasks\n",numThreads,activeCount,idleCount,getNumPendingTasks())); +} + +bool TaskQueue::isIdle() +{ + return (activeCount==0 && getNumPendingTasks()==0 && getNumFinishedTasks()==0 && !paused); +} + +void TaskQueue::pause() +{ + pauseLock.enter(); + paused=true; + pauseLock.leave(); +} + +void TaskQueue::unpause() +{ + pauseLock.enter(); + if (paused) + { + paused=false; + pauseSignal.broadcast(); + } + pauseLock.leave(); +} + +void TaskQueue::enableWorkerThreadsLogging(bool enabled) +{ + ms_workerThreadsLoggingEnabled=enabled; +} diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTaskQueue.h b/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTaskQueue.h new file mode 100644 index 00000000..a14b6f6c --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTaskQueue.h @@ -0,0 +1,149 @@ +#ifndef _DB_TASK_QUEUE_H +#define _DB_TASK_QUEUE_H + +#include +#include + +#include "sharedSynchronization/Mutex.h" +#include "sharedSynchronization/ConditionVariable.h" +#include "sharedThread/ThreadHandle.h" + +namespace DB +{ + +class Server; +class Session; + +class TaskRequest; +class TaskWorkerThread; + +/** A class organizing a queue of requests to the database. Worker threads will +* perform the requests on a FIFO basis. +*/ +class TaskQueue +{ + unsigned int numThreads; + DB::Server *server; + + std::list requestQ; + Mutex requestQLock; + ConditionVariable requestQAdded; + std::list resultQ; + Mutex resultQLock; + Mutex pauseLock; + ConditionVariable pauseSignal; + + std::vector workers; + + /** Set to true when we are trying to delete the queue. Worker + * threads test this to see whether to quit. + */ + bool shutdown; + bool paused; + + Mutex updateCountLock; + int activeCount; + int idleCount; + + int threadGroupId; + + int numRequests; + int numResults; + + Mutex threadIdLock; + int nextThreadId; + + static bool ms_workerThreadsLoggingEnabled; + +public: + TaskQueue(unsigned int _numThreads, Server *_server, int _threadGroupId); + ~TaskQueue(); + + /** Give a request to the task queue. It will hand the request to a + * worker thread to be processed. + */ + void asyncRequest(TaskRequest *req); + + /** Called by the worker thread(s). Pops a request off the queue and + * calls Process() on the request. Sleeps until a request is added if + * there are none available. + * + * Always returns true. TODO: Add support for aborting the worker + * threads, in which case this will return false when the thread should + * stop itself. + * + * @param session A database session owned by the worker thread. It + * will be used for running any queries. + */ + void workerThreadLoop(); + + /** + * The user of the queue should call this periodically. It pops all + * completed tasks off of the queue and invokes their OnComplete() + * functions. + * + * @param maxTime The maximum time to spend processing, or 0 for unlimited. + * If the time limit is reached, tasks will be left on the queue for a + * later call to update() . + */ + void update(float maxTime); + + /** + * Remove all pending tasks that have not been started yet. + * If a task is currently in progress, it is not affected. + */ + void cancel(); + + /** + * Pause the queue. No new tasks will be processed until the queue is unpaused. + */ + void pause(); + + /** + * Unpause the queue. + */ + void unpause(); + + /** + * Count the number of tasks that have not yet been assigned to a + * worker thread. + */ + int getNumPendingTasks(); + + /** + * Count the number of tasks that have been finished but not yet + * processed by the main thred. + */ + int getNumFinishedTasks(); + + /** + * Count the number of threads that are actively working on a task + */ + int getNumActiveTasks(); + + /** + * Return true if the task queue is not doing anything. (No active + * threads and no pending tasks. + */ + bool isIdle(); + + void report(); + + static void enableWorkerThreadsLogging(bool enabled); + + + static int getTotalNumRequests() { return (sm_iTotalNumRequests < 0 ? 0 : sm_iTotalNumRequests); } + static int getTotalNumResults() { return (sm_iTotalNumResults < 0 ? 0 : sm_iTotalNumResults); } + + private: + TaskQueue(); //disable + TaskQueue(const TaskQueue &); //disable + TaskQueue& operator=(const TaskQueue&); //disable + + + static volatile int sm_iTotalNumRequests; + static volatile int sm_iTotalNumResults; +}; + +} +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTaskRequest.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTaskRequest.cpp new file mode 100644 index 00000000..9c324790 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTaskRequest.cpp @@ -0,0 +1,101 @@ +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbTaskRequest.h" + +#include "sharedDatabaseInterface/DbException.h" +#include "sharedDatabaseInterface/DbServer.h" +#include "sharedDatabaseInterface/DbTestQuery.h" +#include "sharedDatabaseInterface/DbSession.h" +#include "sharedFoundation/Os.h" +#include "sharedLog/Log.h" + +DB::TaskRequest::TaskRequest() : + needsProcessing(true), + errorCount(0), + reconnectErrorCount(0) +{ +} + +DB::TaskRequest::~TaskRequest() +{ +} + +void DB::TaskRequest::workerThreadLoop(DB::Session *ses) +{ + int i = 0; + + while (needsProcessing) + { + i++; + if (process(ses)) + { + needsProcessing=false; + errorCount=0; + if (i > 1) + { + LOG("DatabaseError", ("Task succeeded after %d tries", i)); + ses->setFatalOnError(false); + } + } + else + { + // save off the failed query statement from the process() that failed + // because it will be overwritten by the test query statement + std::string failedQuery = ses->getLastQueryStatement(); + + ses->rollbackTransaction(); + Os::sleep(DB::Server::getErrorSleepTime()); + DBTestQuery qry; + if (ses->exec(&qry)) + { + if (qry.fetch() > 0) + { + int result; + qry.value.getValue(result); + if (result == 1) + { + // the connection is good, but the query in process() failed + errorCount++; + LOG("DatabaseError", (" -- DBTestQuery succeeded, errorCount = %d", errorCount)); + if (errorCount==2) + ses->setFatalOnError(true); + continue; + } + } + } + else + LOG("DatabaseError", (" -- DBTestQuery failed, will disconnect/reconnect")); + ses->disconnect(); + while (! (ses->connect())) + { + ses->disconnect(); + Os::sleep(DB::Server::getDisconnectSleepTime()); + }; + LOG("DatabaseError", ("Database connection reestablished")); + } + } + ses->updateServerQueryCount(); + ses->reset(); +} + +bool DB::TaskRequest::mainThreadFinish() +{ + bool allDone=true; + + if (!isFinished()) + { + allDone=false; + needsProcessing=true; + } + return allDone; +} + +void DB::TaskRequest::mainThreadOnComplete() +{ + onComplete(); +} + +bool DB::TaskRequest::isFinished() +{ + return true; +} + diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTaskRequest.h b/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTaskRequest.h new file mode 100644 index 00000000..be75d94e --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTaskRequest.h @@ -0,0 +1,77 @@ +#ifndef _DB_TASK_REQUEST_H +#define _DB_TASK_REQUEST_H + +namespace DB +{ + +class Session; + +/** Abstract base class representing a request sent to the database task queue. + */ +class TaskRequest +{ + private: + /** Flag to avoid calling Process() twice unexpectedly. + */ + bool needsProcessing; + + /** Error count to prevent infinite retries of an operation that always fails. + */ + int errorCount; + int reconnectErrorCount; + + /** Called by the database thread. Will invoke Process(). Do not override. + */ + void workerThreadLoop(DB::Session *session); + + /** Called by the main thread. Will invoke IsFinished(). Do not override. + */ + bool mainThreadFinish(); + + /** Called by the main thread. Will invoke OnComplete(). Do not override. + */ + void mainThreadOnComplete(); + + public: + TaskRequest(); + virtual ~TaskRequest(); + + /** Called by the database thread. Should do whatever database work + * is needed to handle the request. + * + * This will only get executed once, unless IsFinished() returns false. + */ + virtual bool process(DB::Session *session)=0; + + /** Called by the main thread after Process() has completed. Should check the + * results. It should return true if the task is done, or false if the task + * should be put back on the queue for a worker thread to do more processing. + * + * It is not guaranteed that OnComplete() will be invoked if IsFinished() returns + * true, because a subtask's or parent task's IsFinished() might return false, + * requiring the whole set of tasks to be put back on the queue for the worker thread. + * Therefore, IsFinished() could be called again even after it returns true. (And, since + * things may have changed in the mean time, it may need to do its checks again.) + * + * The default implementation does nothing and returns true. + */ + virtual bool isFinished(); + + /** Called by the main thread (from DB::TaskQueue::Update()) after the request + * has been completed and IsFinished() has returned true. OnComplete() will only + * be invoked once. + * + * It is guaranteed that if OnComplete() is invoked at all, it will be invoked immediately + * after IsFinished() returns true. The only things that may happen between IsFinished() + * and OnComplete() is IsFinished() or OnComplete() on another task. + * + * However, it is not guaranteed that OnComplete() will be invoked at all when IsFinished() + * returns true, because subtasks might require more procssing. + */ + virtual void onComplete()=0; + + friend class TaskQueue; +}; + +} +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTestQuery.cpp b/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTestQuery.cpp new file mode 100644 index 00000000..244da29e --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTestQuery.cpp @@ -0,0 +1,34 @@ +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "sharedDatabaseInterface/DbTestQuery.h" + +void DBTestQuery::getSQL(std::string &sql) +{ + sql=std::string("select 1 from dual"); +} + +bool DBTestQuery::bindParameters() +{ + return true; +} + +bool DBTestQuery::bindColumns() +{ + + if (!bindCol(value)) return false; + + return true; +} + +DB::Query::QueryMode DBTestQuery::getExecutionMode() const +{ + return MODE_SQL; +} + +// ---------------------------------------------------------------------- + +DBTestQuery::DBTestQuery() : + Query(), + value() +{ +} + diff --git a/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTestQuery.h b/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTestQuery.h new file mode 100644 index 00000000..cca5e891 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/shared/queue/DbTestQuery.h @@ -0,0 +1,23 @@ +#ifndef _DB_TEST_QUERY_H +#define _DB_TEST_QUERY_H + +#include "sharedDatabaseInterface/Bindable.h" +#include "sharedDatabaseInterface/DbQuery.h" + +class DBTestQuery : public DB::Query +{ +public: + DB::BindableLong value; + + DBTestQuery(); + virtual void getSQL(std::string &sql); + virtual bool bindParameters(); + virtual bool bindColumns(); + virtual QueryMode getExecutionMode() const; + +private: //disable + DBTestQuery(const DBTestQuery&); + DBTestQuery& operator=(const DBTestQuery&); +}; + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src/win32/FirstSharedDatabaseInterface.cpp b/engine/shared/library/sharedDatabaseInterface/src/win32/FirstSharedDatabaseInterface.cpp new file mode 100644 index 00000000..46137c3b --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/win32/FirstSharedDatabaseInterface.cpp @@ -0,0 +1,9 @@ +// ====================================================================== +// +// FirstServerDatabaseInterface.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" + diff --git a/engine/shared/library/sharedDatabaseInterface/src/win32/SQLC_Defs.h b/engine/shared/library/sharedDatabaseInterface/src/win32/SQLC_Defs.h new file mode 100644 index 00000000..6722c280 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src/win32/SQLC_Defs.h @@ -0,0 +1,19 @@ +/* SQLC_Defs.h + * + * This file includes common definitions needed by the other SQLClasses header files. + * (Don't include this file directly -- files that require it will include it.) + * + * Note that this file is specific to each OS, because what header files are + * needed varies in each OS. In particular, Unix versions don't want windows.h. + * + * ODBC versions: includes definitions of ODBC datatypes needed by the various + * SQLClasses + */ + +#ifndef _SQLC_DEFS_H +#define _SQLC_DEFS_H + +#include +#include + +#endif \ No newline at end of file diff --git a/engine/shared/library/sharedDatabaseInterface/src_oci/CMakeLists.txt b/engine/shared/library/sharedDatabaseInterface/src_oci/CMakeLists.txt new file mode 100644 index 00000000..e3312146 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src_oci/CMakeLists.txt @@ -0,0 +1,26 @@ + +set(SHARED_SOURCES + DbBindableVarray.cpp + DbBindableVarray.h + OciQueryImplementation.cpp + OciQueryImplementation.h + OciServer.cpp + OciServer.h + OciSession.cpp + OciSession.h +) + +include_directories( + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedDebug/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundation/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundationTypes/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedLog/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMemoryManager/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedSynchronization/include/public + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/unicode/include +) + +add_library(sharedDatabaseInterface_oci STATIC + ${SHARED_SOURCES} + ${PLATFORM_SOURCES} +) diff --git a/engine/shared/library/sharedDatabaseInterface/src_oci/DbBindableVarray.cpp b/engine/shared/library/sharedDatabaseInterface/src_oci/DbBindableVarray.cpp new file mode 100644 index 00000000..7fff3387 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src_oci/DbBindableVarray.cpp @@ -0,0 +1,591 @@ +// ====================================================================== +// +// DbBindableVarray.cpp +// copyright (c) 2003 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "DbBindableVarray.h" + +#include "OciServer.h" +#include "OciSession.h" +#include "sharedFoundation/NetworkId.h" +#include "sharedLog/Log.h" +#include + +// ====================================================================== + +using namespace DB; + +// ====================================================================== + +BindableVarray::BindableVarray() : + m_initialized(false), + m_tdo (NULL), + m_data (NULL), + m_session (NULL) +{ +} + +// ---------------------------------------------------------------------- + +BindableVarray::~BindableVarray() +{ + if (m_initialized) + free(); +} + +// ---------------------------------------------------------------------- + +bool BindableVarray::create(DB::Session *session, const std::string &name, const std::string &schema) +{ + m_initialized = true; + NOT_NULL(session); + m_session = session; + OCISession *localSession = safe_cast(session); + + if (! (localSession->m_server->checkerr(*localSession, OCITypeByName (localSession->envhp, + localSession->errhp, + localSession->svchp, + reinterpret_cast(const_cast(schema.c_str())), + schema.length(), + reinterpret_cast(const_cast(name.c_str())), + name.length(), + NULL, + 0, + OCI_DURATION_SESSION, + OCI_TYPEGET_HEADER, + &m_tdo)))) + return false; + + if (! (localSession->m_server->checkerr(*localSession, + OCIObjectNew (localSession->envhp, + localSession->errhp, + localSession->svchp, + OCI_TYPECODE_VARRAY, + m_tdo, NULL, + OCI_DURATION_DEFAULT, + true, + reinterpret_cast(&m_data))))) + return false; + + NOT_NULL(m_tdo); + NOT_NULL(m_data); + + return true; +} + +// ---------------------------------------------------------------------- + +void BindableVarray::free() +{ + OCISession *localSession = safe_cast(m_session); + + IGNORE_RETURN(localSession->m_server->checkerr(*localSession, + OCIObjectFree (localSession->envhp, + localSession->errhp, m_data, 0))); + m_initialized = false; + m_tdo=NULL; + m_data=NULL; + m_session=NULL; +} + +// ---------------------------------------------------------------------- + +void BindableVarray::clear() +{ + OCISession *localSession = safe_cast(m_session); + sb4 size=0; + + if (! (localSession->m_server->checkerr(*localSession, OCICollSize(localSession->envhp, localSession->errhp, m_data, &size)))) + return; + if (! (localSession->m_server->checkerr(*localSession, OCICollTrim(localSession->envhp, localSession->errhp, size, m_data)))) + return; +} + +// ---------------------------------------------------------------------- + +OCIArray** BindableVarray::getBuffer() +{ + return &m_data; +} + +// ---------------------------------------------------------------------- + +OCIType* BindableVarray::getTDO() +{ + return m_tdo; +} + +// ---------------------------------------------------------------------- + +bool BindableVarrayNumber::push_back(int value) +{ + OCINumber buffer; + + OCIInd buffer_indicator (OCI_IND_NOTNULL); + + OCISession *localSession = safe_cast(m_session); + + if (! (localSession->m_server->checkerr(*localSession, OCINumberFromInt(localSession->errhp, &value, sizeof(value), OCI_NUMBER_SIGNED, &buffer)))) + return false; + if (! (localSession->m_server->checkerr(*localSession, OCICollAppend(localSession->envhp, localSession->errhp, &buffer, &buffer_indicator, m_data)))) + return false; + + return true; +} + +// ---------------------------------------------------------------------- + +bool BindableVarrayNumber::push_back(long int value) +{ + OCINumber buffer; + + OCIInd buffer_indicator (OCI_IND_NOTNULL); + + OCISession *localSession = safe_cast(m_session); + + if (! (localSession->m_server->checkerr(*localSession, OCINumberFromInt(localSession->errhp, &value, sizeof(value), OCI_NUMBER_SIGNED, &buffer)))) + return false; + if (! (localSession->m_server->checkerr(*localSession, OCICollAppend(localSession->envhp, localSession->errhp, &buffer, &buffer_indicator, m_data)))) + return false; + + return true; +} +// ---------------------------------------------------------------------- + +bool BindableVarrayNumber::push_back(int64 value) +{ + OCINumber buffer; + + OCIInd buffer_indicator (OCI_IND_NOTNULL); + + OCISession *localSession = safe_cast(m_session); + + if (! (localSession->m_server->checkerr(*localSession, OCINumberFromInt(localSession->errhp, &value, sizeof(value), OCI_NUMBER_SIGNED, &buffer)))) + return false; + if (! (localSession->m_server->checkerr(*localSession, OCICollAppend(localSession->envhp, localSession->errhp, &buffer, &buffer_indicator, m_data)))) + return false; + + return true; +} + +// ---------------------------------------------------------------------- + +bool BindableVarrayNumber::push_back(double value) +{ + if (finite(value)==0) + { + if (DB::Server::getFatalOnDataError()) + { + FATAL(true,("DatabaseError: Attempt to save a non-finite value to the database.")); + } + else + { + value=0; + WARNING_STACK_DEPTH(true,(INT_MAX,"DatabaseError: Attempt to save a non-finite value to the database.")); + } + } + + OCINumber buffer; + + OCIInd buffer_indicator (OCI_IND_NOTNULL); + + OCISession *localSession = safe_cast(m_session); + + if (! (localSession->m_server->checkerr(*localSession, OCINumberFromReal(localSession->errhp, &value, sizeof(value), &buffer)))) + return false; + if (! (localSession->m_server->checkerr(*localSession, OCICollAppend(localSession->envhp, localSession->errhp, &buffer, &buffer_indicator, m_data)))) + return false; + + return true; +} + +// ---------------------------------------------------------------------- + +bool BindableVarrayNumber::push_back(bool IsNULL, int value) +{ + OCINumber buffer; + + OCIInd buffer_indicator; + + if ( IsNULL ) + { + buffer_indicator = OCI_IND_NULL; + } + else + { + buffer_indicator = OCI_IND_NOTNULL; + } + OCISession *localSession = safe_cast(m_session); + + if (! (localSession->m_server->checkerr(*localSession, OCINumberFromInt(localSession->errhp, &value, sizeof(value), OCI_NUMBER_SIGNED, &buffer)))) + return false; + if (! (localSession->m_server->checkerr(*localSession, OCICollAppend(localSession->envhp, localSession->errhp, &buffer, &buffer_indicator, m_data)))) + return false; + + return true; +} + +// ---------------------------------------------------------------------- + +bool BindableVarrayNumber::push_back(bool IsNULL, long int value) +{ + OCINumber buffer; + + OCIInd buffer_indicator; + + if ( IsNULL ) + { + buffer_indicator = OCI_IND_NULL; + } + else + { + buffer_indicator = OCI_IND_NOTNULL; + } + + OCISession *localSession = safe_cast(m_session); + + if (! (localSession->m_server->checkerr(*localSession, OCINumberFromInt(localSession->errhp, &value, sizeof(value), OCI_NUMBER_SIGNED, &buffer)))) + return false; + if (! (localSession->m_server->checkerr(*localSession, OCICollAppend(localSession->envhp, localSession->errhp, &buffer, &buffer_indicator, m_data)))) + return false; + + return true; +} +// ---------------------------------------------------------------------- + +bool BindableVarrayNumber::push_back(bool IsNULL, int64 value) +{ + OCINumber buffer; + + OCIInd buffer_indicator; + + if ( IsNULL ) + { + buffer_indicator = OCI_IND_NULL; + } + else + { + buffer_indicator = OCI_IND_NOTNULL; + } + + OCISession *localSession = safe_cast(m_session); + + if (! (localSession->m_server->checkerr(*localSession, OCINumberFromInt(localSession->errhp, &value, sizeof(value), OCI_NUMBER_SIGNED, &buffer)))) + return false; + if (! (localSession->m_server->checkerr(*localSession, OCICollAppend(localSession->envhp, localSession->errhp, &buffer, &buffer_indicator, m_data)))) + return false; + + return true; +} + +// ---------------------------------------------------------------------- + +bool BindableVarrayNumber::push_back(bool IsNULL, double value) +{ + if (!IsNULL && finite(value)==0) + { + if (DB::Server::getFatalOnDataError()) + { + FATAL(true,("DatabaseError: Attempt to save a non-finite value to the database.")); + } + else + { + value=0; + WARNING_STACK_DEPTH(true,(INT_MAX,"DatabaseError: Attempt to save a non-finite value to the database.")); + } + } + + OCINumber buffer; + + OCIInd buffer_indicator; + + if ( IsNULL ) + { + buffer_indicator = OCI_IND_NULL; + } + else + { + buffer_indicator = OCI_IND_NOTNULL; + } + + OCISession *localSession = safe_cast(m_session); + + if (! (localSession->m_server->checkerr(*localSession, OCINumberFromReal(localSession->errhp, &value, sizeof(value), &buffer)))) + return false; + if (! (localSession->m_server->checkerr(*localSession, OCICollAppend(localSession->envhp, localSession->errhp, &buffer, &buffer_indicator, m_data)))) + return false; + + return true; +} + +// ====================================================================== + +BindableVarrayString::BindableVarrayString() : + BindableVarray(), + m_maxLength(0) +{ +} + +// ---------------------------------------------------------------------- + +std::string BindableVarrayNumber::outputValue() const +{ + OCISession *localSession = safe_cast(m_session); + sb4 size; + std::string result("["); + + if (! (localSession->m_server->checkerr(*localSession, OCICollSize(localSession->envhp, localSession->errhp, m_data, &size)))) + return "[*ERROR*]"; + + for (sb4 i=0; i < size; ++i) + { + OCINumber *element; + OCIInd *indicator; + boolean exists; + + if (i!=0) + result += ", "; + + if (localSession->m_server->checkerr(*localSession, OCICollGetElem(localSession->envhp, localSession->errhp, m_data, i, &exists, reinterpret_cast(&element), reinterpret_cast(&indicator)))) + { + if (exists) + { + if (*indicator == OCI_IND_NULL) + result += "NULL"; + else + { + double value; + if (localSession->m_server->checkerr(*localSession, OCINumberToReal(localSession->errhp, element, sizeof(value), &value))) + { + char buffer[100]; + snprintf(buffer,sizeof(buffer),"%f",value); + buffer[sizeof(buffer)-1]='\0'; + result += buffer; + } + else + result += "*ERROR*"; + } + } + else + result += "*NO ELEMENT*"; + } + else + result += "*ERROR*"; + } + result+=']'; + return result; +} + +// ---------------------------------------------------------------------- + +bool BindableVarrayString::create(DB::Session *session, const std::string &name, const std::string &schema, size_t maxLength) +{ + m_maxLength=maxLength; + return BindableVarray::create(session,name,schema); +} + +// ---------------------------------------------------------------------- + +bool BindableVarrayString::push_back(const Unicode::String &value) +{ + std::string str; + str = Unicode::wideToUTF8(value, str); + return push_back(str); +} + +// ---------------------------------------------------------------------- + +bool BindableVarrayString::push_back(const std::string &value) +{ + OCIString *buffer = NULL; + OCIInd buffer_indicator (OCI_IND_NOTNULL); + OCISession *localSession = safe_cast(m_session); + + size_t effectiveLength=value.length(); + if (effectiveLength > m_maxLength) + { + if (DB::Server::getFatalOnDataError()) + { + FATAL(true,("DatabaseError: Attempt to save too long a string to the database: \"%s\"",value.c_str())); + } + else + { + WARNING_STACK_DEPTH(true,(INT_MAX, "DatabaseError: Attempt to save too long a string to the database. (Text is in the log output.)")); + LogManager::logLongText("DatabaseError",std::string("String from previous error is \"")+value+"\""); + effectiveLength = m_maxLength; + } + } + + if (! (localSession->m_server->checkerr(*localSession, OCIStringAssignText(localSession->envhp, localSession->errhp, reinterpret_cast(const_cast(value.c_str())), effectiveLength, &buffer)))) + return false; + if (! (localSession->m_server->checkerr(*localSession, OCICollAppend(localSession->envhp, localSession->errhp, buffer, &buffer_indicator, m_data)))) + return false; + + return true; +} + +// ---------------------------------------------------------------------- + +bool BindableVarrayString::push_back(bool bvalue) +{ + std::string value; + if (bvalue) + value = "Y"; + else + value = "N"; + + OCIString *buffer = NULL; + OCIInd buffer_indicator (OCI_IND_NOTNULL); + OCISession *localSession = safe_cast(m_session); + + if (! (localSession->m_server->checkerr(*localSession, OCIStringAssignText(localSession->envhp, localSession->errhp, reinterpret_cast(const_cast(value.c_str())), value.length(), &buffer)))) + return false; + if (! (localSession->m_server->checkerr(*localSession, OCICollAppend(localSession->envhp, localSession->errhp, buffer, &buffer_indicator, m_data)))) + return false; + + return true; +} + +// ---------------------------------------------------------------------- + +bool BindableVarrayString::push_back(const NetworkId &value) +{ + return push_back(value.getValueString()); +} + +// ---------------------------------------------------------------------- + +bool BindableVarrayString::push_back(bool IsNULL, const Unicode::String &value) +{ + std::string str; + str = Unicode::wideToUTF8(value, str); + return push_back(IsNULL, str); +} + +// ---------------------------------------------------------------------- + +bool BindableVarrayString::push_back(bool IsNULL, const std::string &value) +{ + OCIString *buffer = NULL; + OCIInd buffer_indicator; + if ( IsNULL ) + { + buffer_indicator = OCI_IND_NULL; + } + else + { + buffer_indicator = OCI_IND_NOTNULL; + } + + size_t effectiveLength=value.length(); + if (effectiveLength > m_maxLength) + { + if (DB::Server::getFatalOnDataError()) + { + FATAL(true,("DatabaseError: Attempt to save too long a string to the database: \"%s\"",value.c_str())); + } + else + { + WARNING_STACK_DEPTH(true,(INT_MAX, "DatabaseError: Attempt to save too long a string to the database. (Text is in the log output.)")); + LogManager::logLongText("DatabaseError",std::string("String from previous error is \"")+value+"\""); + effectiveLength = m_maxLength; + } + } + + OCISession *localSession = safe_cast(m_session); + + if (! (localSession->m_server->checkerr(*localSession, OCIStringAssignText(localSession->envhp, localSession->errhp, reinterpret_cast(const_cast(value.c_str())), effectiveLength, &buffer)))) + return false; + if (! (localSession->m_server->checkerr(*localSession, OCICollAppend(localSession->envhp, localSession->errhp, buffer, &buffer_indicator, m_data)))) + return false; + + return true; +} + +// ---------------------------------------------------------------------- + +bool BindableVarrayString::push_back(bool IsNULL, bool bvalue) +{ + std::string value; + if (bvalue) + value = "Y"; + else + value = "N"; + + OCIString *buffer = NULL; + OCIInd buffer_indicator; + if ( IsNULL ) + { + buffer_indicator = OCI_IND_NULL; + } + else + { + buffer_indicator = OCI_IND_NOTNULL; + } + + OCISession *localSession = safe_cast(m_session); + + if (! (localSession->m_server->checkerr(*localSession, OCIStringAssignText(localSession->envhp, localSession->errhp, reinterpret_cast(const_cast(value.c_str())), value.length(), &buffer)))) + return false; + if (! (localSession->m_server->checkerr(*localSession, OCICollAppend(localSession->envhp, localSession->errhp, buffer, &buffer_indicator, m_data)))) + return false; + + return true; +} + +// ---------------------------------------------------------------------- + +bool BindableVarrayString::push_back(bool IsNULL, const NetworkId &value) +{ + return push_back(IsNULL, value.getValueString()); +} + +// ---------------------------------------------------------------------- + +std::string BindableVarrayString::outputValue() const +{ + OCISession *localSession = safe_cast(m_session); + sb4 size; + std::string result("["); + + if (! (localSession->m_server->checkerr(*localSession, OCICollSize(localSession->envhp, localSession->errhp, m_data, &size)))) + return "[*ERROR*]"; + + for (sb4 i=0; i < size; ++i) + { + OCIString **element; + OCIInd *indicator; + boolean exists; + + if (i!=0) + result += ", "; + + if (localSession->m_server->checkerr(*localSession, OCICollGetElem(localSession->envhp, localSession->errhp, m_data, i, &exists, reinterpret_cast(&element), reinterpret_cast(&indicator)))) + { + if (exists) + { + if (*indicator == OCI_IND_NULL) + result += "NULL"; + else + { + OraText * text = OCIStringPtr(localSession->envhp, *element); + if (text) + result += '"' + std::string(reinterpret_cast(text)) + '"'; + else + result += "NULL"; + } + } + else + result += "*NO ELEMENT*"; + } + else + result += "*ERROR*"; + } + result+=']'; + return result; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src_oci/DbBindableVarray.h b/engine/shared/library/sharedDatabaseInterface/src_oci/DbBindableVarray.h new file mode 100644 index 00000000..a9233b6f --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src_oci/DbBindableVarray.h @@ -0,0 +1,101 @@ +// ====================================================================== +// +// DbBindableVarray.h +// copyright (c) 2003 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DbBindableVarray_H +#define INCLUDED_DbBindableVarray_H + +// ====================================================================== + +#include "sharedDatabaseInterface/Bindable.h" + +// ====================================================================== + +struct OCIColl; +typedef OCIColl OCIArray; +struct OCIType; +class NetworkId; + +// ====================================================================== + +namespace DB +{ + class Session; + + // ====================================================================== + +/** + * Bindable array type. Note: only usable in OCI. In ODBC, calling + * any function on this class will FATAL. + */ + class BindableVarray : public Bindable + { + public: + BindableVarray(); + ~BindableVarray(); + + bool create(DB::Session *session, const std::string &name, const std::string &schema); + + void free(); + void clear(); + + OCIArray ** getBuffer(); + OCIType * getTDO(); + + protected: + bool m_initialized; + OCIType *m_tdo; + OCIArray *m_data; + Session *m_session; + }; + +// ====================================================================== + + class BindableVarrayNumber : public BindableVarray + { + public: + bool push_back(bool IsNULL, int value); + bool push_back(bool IsNULL, double value); + bool push_back(bool IsNULL, long int value); + bool push_back(bool IsNULL, int64 value); + bool push_back(int value); + bool push_back(double value); + bool push_back(long int value); + bool push_back(int64 value); + + virtual std::string outputValue() const; + }; + +// ====================================================================== + + class BindableVarrayString : public BindableVarray + { + public: + BindableVarrayString(); + bool create(DB::Session *session, const std::string &name, const std::string &schema, size_t maxLength); + + bool push_back(bool IsNULL, const Unicode::String &value); + bool push_back(bool IsNULL, const std::string &value); + bool push_back(bool IsNULL, bool value); + bool push_back(bool IsNULL, const NetworkId &value); + bool push_back(const Unicode::String &value); + bool push_back(const std::string &value); + bool push_back(bool value); + bool push_back(const NetworkId &value); + + virtual std::string outputValue() const; + + private: + size_t m_maxLength; + }; + +// ====================================================================== + +} //namespace + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src_oci/OciQueryImplementation.cpp b/engine/shared/library/sharedDatabaseInterface/src_oci/OciQueryImplementation.cpp new file mode 100644 index 00000000..e2b27f47 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src_oci/OciQueryImplementation.cpp @@ -0,0 +1,753 @@ +// ====================================================================== +// +// OCIQueryImpl.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "OciQueryImplementation.h" + +#include + +#include "sharedDatabaseInterface/Bindable.h" +#include "sharedDatabaseInterface/DbBindableVarray.h" +#include "sharedDatabaseInterface/DbProtocol.def" +#include "sharedDatabaseInterface/DbQuery.h" +#include "OciServer.h" +#include "OciSession.h" +#include "sharedLog/Log.h" + +// ====================================================================== + +DB::OCIQueryImpl::OCIQueryImpl(Query *query, bool autocommit) : + QueryImpl(query), m_stmthp(0), m_cursorhp(0), m_procBind(0), m_session(0), m_server(0), m_inUse(false), numRowsFetched(0), + nextColumn(1), nextParameter(1), m_endOfData(false), m_dataReady(false), m_autocommit(autocommit), + m_skipSize(0), + m_numElements(1), + m_sql() +{ +} + +// ---------------------------------------------------------------------- + +DB::OCIQueryImpl::~OCIQueryImpl() +{ + if (m_stmthp) + done(); + + for (BindRecListType::iterator i=bindRecList.begin(); i!=bindRecList.end(); ++i) + { + delete (*i); + } +} + + +// ---------------------------------------------------------------------- + +bool DB::OCIQueryImpl::setup(Session *session) +{ +// setSession(session); + + DEBUG_FATAL(m_session!=0,("m_session was not NULL")); + DEBUG_FATAL(m_server!=0,("m_server was not NULL")); + DEBUG_FATAL(m_stmthp!=0,("m_stmthp was not 0")); + DEBUG_FATAL(m_cursorhp!=0,("m_cursorhp was not 0")); + + m_session=dynamic_cast(session); + FATAL((m_session==0),("Must pass a non-NULL OCISession to setup().")); + + m_server=m_session->m_server; + NOT_NULL(m_server); + + if (!m_server->checkerr(*m_session, OCIHandleAlloc( m_session->envhp, + reinterpret_cast(&m_stmthp), + OCI_HTYPE_STMT, 0, 0))) + return false; + + + m_cursorhp=m_stmthp; // later on, we'll separate these two if needed + + return true; +} + +// ---------------------------------------------------------------------- + +bool DB::OCIQueryImpl::prepare() +{ + NOT_NULL(m_server); + NOT_NULL(m_stmthp); + + m_query->getSQL(m_sql); // note: do not change m_sql until done() is called, because Oracle keeps a pointer to it + DEBUG_FATAL(m_sql.size()==0,("Query did not set a SQL statement.\n")); + + if (!m_server->checkerr(*m_session, + OCIStmtPrepare(m_stmthp, + m_session->errhp, + reinterpret_cast(const_cast(m_sql.c_str())), + strlen(m_sql.c_str()), + (ub4) OCI_NTV_SYNTAX, + (ub4) OCI_DEFAULT))) + return false; + if (m_query->getMode()==Query::MODE_PLSQL_REFCURSOR) + { + m_cursorhp=0; // may not be necessary -- OCI docs unclear + if (!m_server->checkerr(*m_session, OCIHandleAlloc( m_session->envhp, (dvoid **) &m_cursorhp, + OCI_HTYPE_STMT, 0, 0))) + return false; + + if (!m_server->checkerr(*m_session, + OCIBindByPos (m_stmthp, + &m_procBind, + m_session->errhp, + nextParameter++, // 1st parameter must be the cursor + &m_cursorhp, + 0, + SQLT_RSET, + 0, + 0, + 0, + 0, + 0, + OCI_DEFAULT))) + return false; + } + + return true; +} + +// ---------------------------------------------------------------------- + +void DB::OCIQueryImpl::preprocessBinds() +{ + //Preprocess binds & defines + for (BindRecListType::iterator i=bindRecList.begin(); i!=bindRecList.end(); ++i) + { + if ((*i)->owner->isNull()) + (*i)->indicator=-1; + else + { + (*i)->length=(unsigned short)*((*i)->owner->getIndicator()); //TODO: something better + (*i)->indicator=1; + if ((*i)->stringAdjust) + ++(*i)->length; //TODO: This feels like a hack + } + } +} + +// ---------------------------------------------------------------------- + +bool DB::OCIQueryImpl::exec() +{ + NOT_NULL(m_server); + NOT_NULL(m_stmthp); + + Query::QueryMode mode=m_query->getMode(); + FATAL((mode!=Query::MODE_SQL) && (mode!=Query::MODE_DML) && (mode!=Query::MODE_PROCEXEC) && (mode!=Query::MODE_PLSQL_REFCURSOR), + ("OCI query is in mode %i, not supported.\n",mode)); + + if (!m_inUse) // if the query was just run, we don't need to do the setup again + { + if (!prepare()) return false; + if (!m_query->bindParameters()) return false; + } + + if (mode==Query::MODE_SQL) + if (!m_query->bindColumns()) return false; + + preprocessBinds(); + + if (m_server->isPrefetchEnabled()) + { + int numRowsToPrefetch=m_server->getPrefetchRows(); + int prefetchMemory=m_server->getPrefetchMemory(); + if (!m_server->checkerr(*m_session, OCIAttrSet((dvoid *) m_stmthp, (ub4) OCI_HTYPE_STMT, + &numRowsToPrefetch, sizeof(int), + (ub4) OCI_ATTR_PREFETCH_ROWS, m_session->errhp))) + { + LOG("DatabaseError", ("Unable to set OCI_ATTR_PREFETCH_ROWS to %i", numRowsToPrefetch)); + return false; + } + if (!m_server->checkerr(*m_session, OCIAttrSet((dvoid *) m_stmthp, (ub4) OCI_HTYPE_STMT, + &prefetchMemory, sizeof(int), + (ub4) OCI_ATTR_PREFETCH_MEMORY, m_session->errhp))) + { + LOG("DatabaseError", ("Unable to set OCI_ATTR_PREFETCH_MEMORY to %i", prefetchMemory)); + return false; + } + } + + m_session->setOkToFetch(); + m_session->setLastQueryStatement(m_sql); + sword status=OCIStmtExecute(m_session->svchp, m_stmthp, m_session->errhp, 1, 0, + NULL, NULL, OCI_DEFAULT); + + if (status == OCI_NO_DATA) + { + if (mode==Query::MODE_PLSQL_REFCURSOR) + { + WARNING_STRICT_FATAL(!m_session->isOkToFetch(),("Calling fetch after commit, without an execute in between (may cause Oracle to crash).")); + sword status=OCIStmtFetch (m_cursorhp, m_session->errhp, 0, OCI_FETCH_NEXT, OCI_DEFAULT); + if (!m_server->checkerr(*m_session,status)) + return false; + } + m_endOfData=true; + } + else + { + if (m_server->checkerr(*m_session,status)) + { + if (mode==Query::MODE_PLSQL_REFCURSOR) + { + if (!m_query->bindColumns()) return false; + + m_dataReady=false; // in this mode, Oracle doesn't implicitly fetch a row + m_endOfData=false; + } + else + { + postProcessResults(); // in this mode, Oracle implicitly fetches the first row + + m_endOfData=false; + m_dataReady=true; + } + } + else + return false; + } + + m_inUse=true; + return true; +} + +// ---------------------------------------------------------------------- + +int DB::OCIQueryImpl::fetch() +{ + DEBUG_FATAL((m_query->getMode()!=Query::MODE_SQL) && (m_query->getMode()!=Query::MODE_PLSQL_REFCURSOR), + ("Called fetch() on a query not in a fetch-compatible mode.")); + + if (m_dataReady) // a row was already fetched implicitly by exec + { + m_dataReady = false; + return 1; + } + + if (m_endOfData) return 0; + + WARNING_STRICT_FATAL(!m_session->isOkToFetch(),("Calling fetch after commit, without an execute in between (may cause Oracle to crash).")); + sword status=OCIStmtFetch (m_cursorhp, m_session->errhp, m_numElements, OCI_FETCH_NEXT, OCI_DEFAULT); + + if (status == OCI_NO_DATA) + { + ub4 rows; + ub4 sizep = sizeof(ub4); + OCIAttrGet((dvoid *) m_cursorhp, (ub4) OCI_HTYPE_STMT, + (dvoid *)& rows, (ub4 *) &sizep, OCI_ATTR_ROWS_FETCHED, m_session->errhp); + + sword status=OCIStmtFetch (m_cursorhp, m_session->errhp, 0, OCI_FETCH_NEXT, OCI_DEFAULT); // cancel the cursor + if (!m_server->checkerr(*m_session,status)) + return -1; + m_endOfData=true; + if (rows!=0) + { + postProcessResults(); + return rows; // last batch of data + } + else + return 0; // no more data + } + else + { + if (!m_server->checkerr(*m_session,status)) + return -1; + } + + ub4 rows; + ub4 sizep = sizeof(ub4); + OCIAttrGet((dvoid *) m_cursorhp, (ub4) OCI_HTYPE_STMT, + (dvoid *)& rows, (ub4 *) &sizep, OCI_ATTR_ROWS_FETCHED, m_session->errhp); + + m_endOfData=false; + postProcessResults(); + return rows; +} + +// ---------------------------------------------------------------------- + + +void DB::OCIQueryImpl::postProcessResults() +{ + if (m_numElements == 1) + { + for (BindRecListType::iterator i=bindRecList.begin(); i!=bindRecList.end(); ++i) + { + if ((*i)->indicator==-1) + (*i)->owner->setNull(); + else + *((*i)->owner->getIndicator())=(*i)->length; + } + } + else + { + for (BindRecListType::iterator i=bindRecList.begin(); i!=bindRecList.end(); ++i) + { + if ((*i)->m_indicatorArray) + { + for (size_t j=0; j(reinterpret_cast((*i)->owner) + m_skipSize * j); // if OCI can do something this ugly, so can I + + if ((*i)->m_indicatorArray[j]==-1) + owner->setNull(); + else + *(owner->getIndicator())=(*i)->m_lengthArray[j]; + } + } + else + { + // this query involves bulk binds, but this parameter is not one of them. Treat it as a single bind. + if ((*i)->indicator==-1) + (*i)->owner->setNull(); + else + *((*i)->owner->getIndicator())=(*i)->length; + + } + } + } +} + +// ---------------------------------------------------------------------- + +void DB::OCIQueryImpl::done() +{ + NOT_NULL(m_stmthp); + + if (m_autocommit) + m_server->checkerr(*m_session, OCITransCommit(m_session->svchp, m_session->errhp, 0)); + + OCIHandleFree(m_stmthp, OCI_HTYPE_STMT); + if (m_cursorhp!=m_stmthp) + OCIHandleFree(m_cursorhp, OCI_HTYPE_STMT); + + for (BindRecListType::iterator i=bindRecList.begin(); i!=bindRecList.end(); ++i) + { + delete (*i); + } + bindRecList.clear(); + + m_session=0; + m_server=0; + m_stmthp=0; + m_cursorhp=0; + nextColumn=1; + nextParameter=1; + m_endOfData=true; + m_inUse=false; +} + +// ---------------------------------------------------------------------- + +int DB::OCIQueryImpl::rowCount() +{ + NOT_NULL(m_stmthp); + + int value; + + m_server->checkerr(*m_session, OCIAttrGet (m_cursorhp, OCI_HTYPE_STMT,&value,0,OCI_ATTR_ROW_COUNT,m_session->errhp)); + + return value; +} + +// ---------------------------------------------------------------------- + +DB::OCIQueryImpl::BindRec *DB::OCIQueryImpl::addBindRec(Bindable &owner) +{ + BindRec *br=new BindRec(m_numElements); + br->owner=&owner; + bindRecList.push_back(br); + return br; +} + +// ---------------------------------------------------------------------- + +DB::Protocol DB::OCIQueryImpl::getProtocol() const +{ + return DB::PROTOCOL_OCI; +} + +// ---------------------------------------------------------------------- + +void DB::OCIQueryImpl::setColArrayMode(size_t skipSize, size_t numElements) +{ + m_skipSize = skipSize; + m_numElements = numElements; +} + +// ====================================================================== + +DB::OCIQueryImpl::BindRec::BindRec(size_t numElements) : + defnp(0), + bindp(0), + indicator(-1), + length(0), + stringAdjust(false), + m_numElements(numElements), + m_indicatorArray(NULL), + m_lengthArray(NULL) +{ + // This is ugly, but it avoids having to create two different kinds of bindrecs that inherit from an abstract base class + if (m_numElements > 1) + { + m_indicatorArray = new int16[m_numElements]; + m_lengthArray = new uint16[m_numElements]; + } +} + +// ---------------------------------------------------------------------- + +DB::OCIQueryImpl::BindRec::~BindRec() +{ + delete[] m_indicatorArray; + delete[] m_lengthArray; +} + +// ---------------------------------------------------------------------- + +int16 * DB::OCIQueryImpl::BindRec::getIndicatorPointer() +{ + if (m_numElements > 1) + return m_indicatorArray; + else + return &indicator; +} + +// ---------------------------------------------------------------------- + +uint16 * DB::OCIQueryImpl::BindRec::getLengthPointer() +{ + if (m_numElements > 1) + return m_lengthArray; + else + return &length; +} + +// ---------------------------------------------------------------------- + +size_t DB::OCIQueryImpl::BindRec::getIndicatorSkipSize() +{ + return reinterpret_cast(&(m_indicatorArray[1])) - reinterpret_cast(&(m_indicatorArray[0])); +} + +// ---------------------------------------------------------------------- + +size_t DB::OCIQueryImpl::BindRec::getLengthSkipSize() +{ + return reinterpret_cast(&(m_lengthArray[1])) - reinterpret_cast(&(m_lengthArray[0])); +} + +// ====================================================================== + +bool DB::OCIQueryImpl::bindCol(BindableLong &buffer) +{ + BindRec *br=addBindRec(buffer); + + m_server->checkerr(*m_session, OCIDefineByPos(m_cursorhp, + &(br->defnp), + m_session->errhp, + nextColumn++, + buffer.getBuffer(), + sizeof(long), + SQLT_INT, + br->getIndicatorPointer(), + br->getLengthPointer(), + (ub2 *)0, + OCI_DEFAULT)); + + if (m_numElements > 1) + { + m_server->checkerr(*m_session, OCIDefineArrayOfStruct(br->defnp, + m_session->errhp, + m_skipSize, + br->getIndicatorSkipSize(), + br->getLengthSkipSize(), + 0)); + } + return true; +} + +// ---------------------------------------------------------------------- + +bool DB::OCIQueryImpl::bindParameter(BindableLong &buffer) +{ + BindRec *br=addBindRec(buffer); + + return m_server->checkerr(*m_session, OCIBindByPos (m_stmthp, + &(br->bindp), + m_session->errhp, + nextParameter++, + buffer.getBuffer(), + sizeof(long), + SQLT_INT, + &(br->indicator), + &(br->length), + 0, + 0, + 0, + OCI_DEFAULT)); +} + +// ---------------------------------------------------------------------- + +bool DB::OCIQueryImpl::bindCol(BindableStringBase &buffer) +{ + BindRec *br=addBindRec(buffer); + + m_server->checkerr(*m_session, OCIDefineByPos(m_cursorhp, + &(br->defnp), + m_session->errhp, + nextColumn++, + buffer.getBuffer(), + buffer.getS()+1, + SQLT_STR, + br->getIndicatorPointer(), + br->getLengthPointer(), + (ub2 *)0, + OCI_DEFAULT)); + + if (m_numElements > 1) + { + m_server->checkerr(*m_session, OCIDefineArrayOfStruct(br->defnp, + m_session->errhp, + m_skipSize, + br->getIndicatorSkipSize(), + br->getLengthSkipSize(), + 0)); + } + return true; +} + +// ---------------------------------------------------------------------- + +bool DB::OCIQueryImpl::bindParameter(BindableStringBase &buffer) +{ + BindRec *br=addBindRec(buffer); + + br->stringAdjust=true; + return m_server->checkerr(*m_session, OCIBindByPos (m_stmthp, + &(br->bindp), + m_session->errhp, + nextParameter++, + buffer.getBuffer(), + buffer.getS()+1, + SQLT_STR, + &(br->indicator), + &(br->length), + 0, + 0, + 0, + OCI_DEFAULT)); +} + +// ---------------------------------------------------------------------- + +bool DB::OCIQueryImpl::bindCol(BindableUnicodeBase &buffer) +{ + BindRec *br=addBindRec(buffer); + + m_server->checkerr(*m_session, OCIDefineByPos(m_cursorhp, + &(br->defnp), + m_session->errhp, + nextColumn++, + buffer.getBuffer(), + buffer.getS()+1, + SQLT_STR, + br->getIndicatorPointer(), + br->getLengthPointer(), + (ub2 *)0, + OCI_DEFAULT)); + + if (m_numElements > 1) + { + m_server->checkerr(*m_session, OCIDefineArrayOfStruct(br->defnp, + m_session->errhp, + m_skipSize, + br->getIndicatorSkipSize(), + br->getLengthSkipSize(), + 0)); + } + return true; +} + +// ---------------------------------------------------------------------- + +bool DB::OCIQueryImpl::bindParameter(BindableUnicodeBase &buffer) +{ + BindRec *br=addBindRec(buffer); + + br->stringAdjust=true; + br->length=static_cast(*(buffer.getIndicator())); + return m_server->checkerr(*m_session, OCIBindByPos (m_stmthp, + &(br->bindp), + m_session->errhp, + nextParameter++, + buffer.getBuffer(), + buffer.getS()+1, + SQLT_STR, + &(br->indicator), + &(br->length), + 0, + 0, + 0, + OCI_DEFAULT)); +} + +// ====================================================================== + +bool DB::OCIQueryImpl::bindCol(BindableDouble &buffer) +{ + BindRec *br=addBindRec(buffer); + + m_server->checkerr(*m_session, OCIDefineByPos(m_cursorhp, + &(br->defnp), + m_session->errhp, + nextColumn++, + buffer.getBuffer(), + sizeof(double), + SQLT_FLT, + br->getIndicatorPointer(), + br->getLengthPointer(), + (ub2 *)0, + OCI_DEFAULT)); + if (m_numElements > 1) + { + m_server->checkerr(*m_session, OCIDefineArrayOfStruct(br->defnp, + m_session->errhp, + m_skipSize, + br->getIndicatorSkipSize(), + br->getLengthSkipSize(), + 0)); + } + return true; +} + +// ---------------------------------------------------------------------- + +bool DB::OCIQueryImpl::bindParameter(BindableDouble &buffer) +{ + BindRec *br=addBindRec(buffer); + + return m_server->checkerr(*m_session, OCIBindByPos (m_stmthp, + &(br->bindp), + m_session->errhp, + nextParameter++, + buffer.getBuffer(), + sizeof(double), + SQLT_FLT, + &(br->indicator), + &(br->length), + 0, + 0, + 0, + OCI_DEFAULT)); +} + +// ---------------------------------------------------------------------- + +bool DB::OCIQueryImpl::bindCol(BindableBool &buffer) +{ + BindRec *br=addBindRec(buffer); + + m_server->checkerr(*m_session, OCIDefineByPos(m_cursorhp, + &(br->defnp), + m_session->errhp, + nextColumn++, + buffer.getBuffer(), + 2, + SQLT_STR, + br->getIndicatorPointer(), + br->getLengthPointer(), + (ub2 *)0, + OCI_DEFAULT)); + + if (m_numElements > 1) + { + m_server->checkerr(*m_session, OCIDefineArrayOfStruct(br->defnp, + m_session->errhp, + m_skipSize, + br->getIndicatorSkipSize(), + br->getLengthSkipSize(), + 0)); + } + return true; +} + +// ---------------------------------------------------------------------- + +bool DB::OCIQueryImpl::bindParameter(BindableBool &buffer) +{ + BindRec *br=addBindRec(buffer); + + br->stringAdjust=true; + return m_server->checkerr(*m_session, OCIBindByPos (m_stmthp, + &(br->bindp), + m_session->errhp, + nextParameter++, + buffer.getBuffer(), + 2, + SQLT_STR, + &(br->indicator), + &(br->length), + 0, + 0, + 0, + OCI_DEFAULT)); +} + +// ---------------------------------------------------------------------- + +bool DB::OCIQueryImpl::bindParameter(BindableVarray &buffer) +{ + BindRec *br=addBindRec(buffer); + + bool result = m_server->checkerr(*m_session, OCIBindByPos (m_stmthp, + &(br->bindp), + m_session->errhp, + nextParameter++, + NULL, + 0, + SQLT_NTY, + NULL, + NULL, + 0, + 0, + 0, + OCI_DEFAULT)); + + if (!result) + return false; + + return m_server->checkerr(*m_session, OCIBindObject (br->bindp, + m_session->errhp, + buffer.getTDO(), + reinterpret_cast(buffer.getBuffer()), + 0, + 0, + 0)); +} + +// ---------------------------------------------------------------------- + +std::string DB::OCIQueryImpl::outputDataValues() const +{ + std::string results; + for (BindRecListType::const_iterator i=bindRecList.begin(); i!=bindRecList.end(); ++i) + { + if (i!=bindRecList.begin()) + results += ", "; + results += (*i)->owner->outputValue(); + } + return results; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src_oci/OciQueryImplementation.h b/engine/shared/library/sharedDatabaseInterface/src_oci/OciQueryImplementation.h new file mode 100644 index 00000000..6cf41e37 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src_oci/OciQueryImplementation.h @@ -0,0 +1,133 @@ +// ====================================================================== +// +// OCIQueryImpl.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_OCIQueryImpl_H +#define INCLUDED_OCIQueryImpl_H + +// ====================================================================== + +#include "sharedDatabaseInterface/DbQueryImplementation.h" +#include +#include + +// ====================================================================== + +namespace DB +{ + class BindableVarray; +} + +// ====================================================================== + +struct OCIStmt; +struct OCIDefine; +struct OCIBind; + +namespace DB +{ + class Bindable; + class OCISession; + class OCIServer; + + class OCIQueryImpl : public QueryImpl + { + public: + OCIQueryImpl(Query *query, bool autocommit); + virtual ~OCIQueryImpl(); + + virtual bool setup(Session *session); + virtual bool prepare(); + virtual int fetch(); + virtual void done(); + virtual int rowCount(); + virtual bool exec(); + + virtual Protocol getProtocol() const; + + virtual void setColArrayMode(size_t skipSize, size_t numElements); + + virtual bool bindCol(BindableLong &buffer); + virtual bool bindParameter(BindableLong &buffer); + virtual bool bindCol(BindableDouble &buffer); + virtual bool bindParameter(BindableDouble &buffer); + virtual bool bindCol(BindableStringBase &buffer); + virtual bool bindParameter(BindableStringBase &buffer); + virtual bool bindCol(BindableUnicodeBase &buffer); + virtual bool bindParameter(BindableUnicodeBase &buffer); + virtual bool bindCol(BindableBool &buffer); + virtual bool bindParameter(BindableBool &buffer); + virtual bool bindParameter(BindableVarray &buffer); + + std::string outputDataValues() const; + + protected: + struct BindRec + { + BindRec(size_t numElements); + ~BindRec(); + + int16 * getIndicatorPointer(); + uint16 * getLengthPointer(); + + size_t getIndicatorSkipSize(); + size_t getLengthSkipSize(); + + Bindable *owner; + OCIDefine *defnp; + OCIBind *bindp; + int16 indicator; + uint16 length; + bool stringAdjust; + size_t m_numElements; + int16 *m_indicatorArray; + uint16 *m_lengthArray; + }; + + typedef std::list BindRecListType; + + protected: + + BindRec *addBindRec(Bindable &owner); + void preprocessBinds(); + void postProcessResults(); + + protected: + /** The OCIStmt handle representing the query. + */ + ::OCIStmt *m_stmthp; + /** The handle representing the cursor containing the results of the query. Usually, + * this is set to the same as stmthp. + */ + ::OCIStmt *m_cursorhp; + ::OCIBind *m_procBind; + + DB::OCISession *m_session; + DB::OCIServer *m_server; + + bool m_inUse; + int numRowsFetched; + int nextColumn; + int nextParameter; + bool m_endOfData; + bool m_dataReady; + BindRecListType bindRecList; + bool m_autocommit; + size_t m_skipSize; + size_t m_numElements; + + std::string m_sql; + + private: + OCIQueryImpl(const OCIQueryImpl&); + OCIQueryImpl & operator = (const OCIQueryImpl &); + }; + +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src_oci/OciServer.cpp b/engine/shared/library/sharedDatabaseInterface/src_oci/OciServer.cpp new file mode 100644 index 00000000..ead0cb9c --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src_oci/OciServer.cpp @@ -0,0 +1,113 @@ +// ====================================================================== +// +// OCIServer.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "OciServer.h" + +#include "OciSession.h" +#include "sharedDatabaseInterface/DbException.h" +#include "sharedLog/Log.h" + +#include + +// ====================================================================== + +DB::OCIServer::OCIServer(const char *_dsn, const char *_uid, const char *_pwd, bool useMemoryManager) : + DB::Server(_dsn,_uid,_pwd, useMemoryManager) +{ + + +} + +// ---------------------------------------------------------------------- + +// Server destructor +DB::OCIServer::~OCIServer() +{ + disconnect(); +} + +// ---------------------------------------------------------------------- + +DB::Session *DB::OCIServer::createSession() +{ + return new DB::OCISession(this); +} + +// ---------------------------------------------------------------------- + +bool DB::OCIServer::checkerr(OCISession const & session, int status) +{ + switch (status) + { + case OCI_SUCCESS: + return true; + case OCI_SUCCESS_WITH_INFO: + REPORT_LOG(true,("Error - OCI_SUCCESS_WITH_INFO\n")); + LOG("DatabaseError", ("Unhandled database error.")); + return false; + case OCI_NEED_DATA: + REPORT_LOG(true,("Error - OCI_NEED_DATA\n")); + LOG("DatabaseError", ("Unhandled database error.")); + return false; + case OCI_NO_DATA: + REPORT_LOG(true,("Error - OCI_NODATA\n")); + LOG("DatabaseError", ("Unhandled database error.")); + return false; + case OCI_ERROR: + { + text errbuf[512]; + sb4 errcode = 0; + + OCIErrorGet((dvoid *)(session.errhp), (ub4) 1, (text *) NULL, &errcode, + errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); + + WARNING(true,("Database error: %.*s",512,errbuf)); + LOG("DatabaseError",("Database error: %.*s",512,errbuf)); + FATAL(DB::Server::getFatalOnError() || session.getFatalOnError(),("Database error: %.*s",512,errbuf)); + + return false; + + /* + switch (errcode) + { + case 1013: + FATAL(true,("Cancelled by user request (ctrl-c or kill signal).\n")); + break; + + case 12541: + REPORT_LOG(true,("Database Error - %.*s\n", 512, errbuf)); + return false; + + default: + FATAL(true,("Unhandled Database Error - %.*s\n", 512, errbuf)); + break; + } + */ + } + case OCI_INVALID_HANDLE: + REPORT_LOG(true,("Error - OCI_INVALID_HANDLE\n")); + LOG("DatabaseError", ("Unhandled database error - OCI_INVALID_HANDLE")); + return false; + case OCI_STILL_EXECUTING: + REPORT_LOG(true,("Error - OCI_STILL_EXECUTE\n")); + LOG("DatabaseError", ("Unhandled database error.")); + return false; + case OCI_CONTINUE: + REPORT_LOG(true,("Error - OCI_CONTINUE\n")); + LOG("DatabaseError", ("Unhandled database error.")); + return false; + default: + REPORT_LOG(true,("Error - unrecognized OCI error state\n")); + LOG("DatabaseError", ("Unhandled database error.")); + return false; + } + return false; +} + + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src_oci/OciServer.h b/engine/shared/library/sharedDatabaseInterface/src_oci/OciServer.h new file mode 100644 index 00000000..0689b3e1 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src_oci/OciServer.h @@ -0,0 +1,53 @@ +// ====================================================================== +// +// OCIServer.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_OCIServer_H +#define INCLUDED_OCIServer_H + +#include "sharedDatabaseInterface/DbServer.h" + +// ====================================================================== + +struct OCIEnv; +struct OCIError; +struct OCIServer; +struct OCISvcCtx; + +namespace DB { + class OCISession; + + class OCIServer : public Server + { + public: + + OCIServer(const char *_dsn, const char *_uid, const char *_pwd, bool useMemoryManager); + virtual ~OCIServer(); + + virtual Session *createSession(); + + /** Check the results from an ODBC call to identify whether the call succeeded. Returns TRUE if everything is OK, i.e. + the call returned SQL_SUCCESS, or it returned SQL_SUCCESS_WITH_INFO and the additional information does not indicate + a problem. + */ + // static bool checkError(int rc, SQLSMALLINT handleType, SQLHANDLE handle); + + static bool checkerr(OCISession const & session, int status); + + private: + + // OCI requires sharing so many handles at different levels that it's easiest to just make everybody friends. + friend class OCISession; + friend class OCIQueryImpl; + OCIServer(const OCIServer &); + OCIServer & operator = (const OCIServer &); + }; + +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDatabaseInterface/src_oci/OciSession.cpp b/engine/shared/library/sharedDatabaseInterface/src_oci/OciSession.cpp new file mode 100644 index 00000000..124998bc --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src_oci/OciSession.cpp @@ -0,0 +1,289 @@ +// ====================================================================== +// +// OCISession.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDatabaseInterface/FirstSharedDatabaseInterface.h" +#include "OciSession.h" + +#include "OciQueryImplementation.h" +#include "OciServer.h" +#include "sharedDatabaseInterface/DbException.h" +#include "sharedFoundation/Os.h" +#include "sharedLog/Log.h" +#include "sharedSynchronization/RecursiveMutex.h" + +#include +#include + +// ====================================================================== + +namespace DB +{ + namespace OCISessionNamespace + { + RecursiveMutex connectLock; + } +} + +using namespace DB::OCISessionNamespace; + +// ====================================================================== + +static dvoid *mallocHook(dvoid *, size_t size) +{ + return reinterpret_cast(new char[size]); +} + +// ---------------------------------------------------------------------- + +static dvoid *reallocHook(dvoid *, dvoid *memptr, size_t newsize) +{ + return reinterpret_cast(MemoryManager::reallocate(memptr, newsize)); +} + +// ---------------------------------------------------------------------- + +static void freeHook(dvoid *, dvoid *memptr) +{ + if (memptr) + delete [] reinterpret_cast(memptr); +} + +// ====================================================================== + +DB::OCISession::OCISession(DB::OCIServer *server) : + m_server(server), + envhp(NULL), + errhp(NULL), + srvhp(NULL), + sesp(NULL), + svchp(NULL), + autoCommitMode(true), + m_resetTime(server->getReconnectTime()==0 ? 0 : time(0) + server->getReconnectTime()), + m_okToFetch(true) +{ +} + +DB::OCISession::~OCISession() +{ + if (connected) + disconnect(); +} + +bool DB::OCISession::connect() +{ + connectLock.enter(); + + // Create OCI environment. We're creating one environment per session, + // so that each session can be used by a different thread concurrently. + + sword result=OCI_ERROR; + if (m_server->getUseMemoryManager()) + { + result = OCIEnvCreate(&envhp, // OCIEnv **envhpp, + OCI_THREADED | OCI_OBJECT, //ub4 mode, //TODO: do we have to use threaded mode? + 0, // CONST dvoid *ctxp, + &mallocHook, // CONST dvoid *(*malocfp) + // (dvoid *ctxp, + // size_t size), + &reallocHook, // CONST dvoid *(*ralocfp) + // (dvoid *ctxp, + // dvoid *memptr, + // size_t newsize), + &freeHook, // CONST void (*mfreefp) + // (dvoid *ctxp, + // dvoid *memptr)) + 0, //size_t xtramemsz, + 0 ); //dvoid **usrmempp ); + } + else + { + result = OCIEnvCreate(&envhp, // OCIEnv **envhpp, + OCI_THREADED | OCI_OBJECT, //ub4 mode, //TODO: do we have to use threaded mode? + 0, // CONST dvoid *ctxp, + 0, // CONST dvoid *(*malocfp) + // (dvoid *ctxp, + // size_t size), + 0, // CONST dvoid *(*ralocfp) + // (dvoid *ctxp, + // dvoid *memptr, + // size_t newsize), + 0, // CONST void (*mfreefp) + // (dvoid *ctxp, + // dvoid *memptr)) + 0, //size_t xtramemsz, + 0 ); //dvoid **usrmempp ); + + } + FATAL(result != OCI_SUCCESS,("OciEnvCreate failed with error code %hd",result)); + + // Create error handle. + OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &errhp, OCI_HTYPE_ERROR, + (size_t) 0, (dvoid **) 0); + + // Create handle to the server and establish connection + OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &srvhp, OCI_HTYPE_SERVER, + (size_t) 0, (dvoid **) 0); + + int retryCount = 0; + connected = false; + while (!connected && retryCount < 5) + { + LOG("DatabaseConnect", ("calling OCIServerAttach() for OCISession=[%p] with dsn=[%s], uid=[%s], pwd=[%s]", this, m_server->getDSN(), m_server->uid, m_server->pwd)); + + connected = DB::OCIServer::checkerr(*this, + OCIServerAttach( srvhp, errhp, reinterpret_cast(const_cast(m_server->getDSN())), strlen(m_server->getDSN()), 0)); + + if (! connected) + { + ++retryCount; + DEBUG_REPORT_LOG(retryCount<5,("Retrying database connection\n")); + Os::sleep(1000); + } + } + if (!connected) + { + LOG("DatabaseError", ("Database error, Failed to connect to database after 5 tries")); + disconnect(); // cleanup + connectLock.leave(); + return false; + } + + // Create a session, set userid & password + OCIHandleAlloc(envhp, (void**) &sesp, + (ub4) OCI_HTYPE_SESSION, (size_t) 0, (dvoid **) 0); + + OCIAttrSet((dvoid *) sesp, (ub4) OCI_HTYPE_SESSION, + (dvoid *) m_server->uid, strlen(m_server->uid), + (ub4) OCI_ATTR_USERNAME, errhp); + + OCIAttrSet((dvoid *) sesp, (ub4) OCI_HTYPE_SESSION, + (dvoid *) m_server->pwd, strlen(m_server->pwd), + (ub4) OCI_ATTR_PASSWORD, errhp); + + // create service context (Wtf is a service context? Don't know, but you gotta have one.) + OCIHandleAlloc( envhp, (dvoid **) &svchp, OCI_HTYPE_SVCCTX, + (size_t) 0, (dvoid **) 0); + + // Service context has pointers to Server and Session + OCIAttrSet( (dvoid *) svchp, OCI_HTYPE_SVCCTX, srvhp, + (ub4) 0, OCI_ATTR_SERVER, errhp); + + OCIAttrSet((dvoid *) svchp, (ub4) OCI_HTYPE_SVCCTX, + (dvoid *) sesp, (ub4) 0, + (ub4) OCI_ATTR_SESSION, errhp); + + // Establish session (i.e. check password) + if (DB::OCIServer::checkerr(*this, OCISessionBegin ( svchp, errhp, sesp, OCI_CRED_RDBMS, (ub4) OCI_DEFAULT))) + { + connected=true; + } + else + { + disconnect(); // cleanup + connected=false; + } + + connectLock.leave(); + return connected; +} + +bool DB::OCISession::disconnect() +{ + connectLock.enter(); + + bool success = true; + + connected=false; + + if (srvhp && errhp && !DB::OCIServer::checkerr(*this,OCIServerDetach( srvhp, errhp, OCI_DEFAULT))) + { + LOG("DatabaseError", ("OCIDetach returned an error.")); + success = false; + } + + if (svchp && (OCIHandleFree(svchp,OCI_HTYPE_SVCCTX) != OCI_SUCCESS)) + { + LOG("DatabaseError", ("OCIHandleFree OCI_HTYPE_SVCCTX returned an error.")); + success = false; + } + + if (sesp && (OCIHandleFree(sesp,OCI_HTYPE_SESSION) != OCI_SUCCESS)) + { + LOG("DatabaseError", ("OCIHandleFree OCI_HTYPE_SESSION returned an error.")); + success = false; + } + + if (srvhp && (OCIHandleFree(srvhp,OCI_HTYPE_SERVER) != OCI_SUCCESS)) + { + LOG("DatabaseError", ("OCIHandleFree OCI_HTYPE_SERVER returned an error.")); + success = false; + } + + if (errhp && (OCIHandleFree(errhp,OCI_HTYPE_ERROR) != OCI_SUCCESS)) + { + LOG("DatabaseError", ("OCIHandleFree OCI_HTYPE_ERROR returned an error.")); + success = false; + } + + if (envhp && (OCIHandleFree(envhp,OCI_HTYPE_ENV) != OCI_SUCCESS)) + { + LOG("DatabaseError", ("OCIHandleFree OCI_HTYPE_ENV returned an error.")); + success = false; + } + + svchp = NULL; + sesp = NULL; + srvhp = NULL; + errhp = NULL; + envhp = NULL; + + connectLock.leave(); + return success; +} + +bool DB::OCISession::setAutoCommitMode(bool autocommit) +{ + autoCommitMode=autocommit; + return true; +} + +bool DB::OCISession::commitTransaction() +{ + m_okToFetch = false; + return m_server->checkerr(*this, OCITransCommit(svchp, errhp, 0)); +} + +bool DB::OCISession::rollbackTransaction() +{ + return m_server->checkerr(*this, OCITransRollback(svchp, errhp, 0)); +} + +bool DB::OCISession::reset() +{ + autoCommitMode = true; + if (m_resetTime !=0 && time(0) > m_resetTime) + { + LOG("Reconnections",("Reconnecting")); + commitTransaction(); // just to be safe -- there shouldn't be an oustanding transaction now anyway + disconnect(); + connect(); + m_resetTime = time(0) + m_server->getReconnectTime(); + } + return true; +} + +DB::QueryImpl *DB::OCISession::createQueryImpl(Query *owner) const +{ + return new DB::OCIQueryImpl(owner, autoCommitMode); +} + +void DB::OCISession::setOkToFetch() +{ + m_okToFetch = true; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDatabaseInterface/src_oci/OciSession.h b/engine/shared/library/sharedDatabaseInterface/src_oci/OciSession.h new file mode 100644 index 00000000..4754c750 --- /dev/null +++ b/engine/shared/library/sharedDatabaseInterface/src_oci/OciSession.h @@ -0,0 +1,76 @@ +// ====================================================================== +// +// OCISession.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_OCISession_H +#define INCLUDED_OCISession_H + +// ====================================================================== + +#include "sharedDatabaseInterface/DbSession.h" + +struct OCIEnv; +struct OCIError; +struct OCIServer; +struct OCISession; +struct OCISvcCtx; + +namespace DB { + class OCIServer; // not to be confused with ::OCIServer + + class OCISession : public Session + { + OCIServer *m_server; + + ::OCIEnv *envhp; + ::OCIError *errhp; + ::OCIServer *srvhp; + ::OCISession *sesp; + ::OCISvcCtx *svchp; + + bool autoCommitMode; + time_t m_resetTime; + bool m_okToFetch; + + // following are disallowed: + OCISession(const OCISession& rhs); + OCISession &operator=(const OCISession& rhs); + + public: + OCISession(OCIServer *server); + virtual ~OCISession(); + + virtual bool connect(); + virtual bool disconnect(); + virtual bool reset(); + virtual bool setAutoCommitMode(bool autocommit); + virtual bool commitTransaction(); + virtual bool rollbackTransaction(); + virtual QueryImpl *createQueryImpl(Query *owner) const; + void setOkToFetch(); + bool isOkToFetch() const; + + // OCI requires sharing so many handles at different levels that it's easiest to just make everybody friends. + friend class OCIQueryImpl; + friend class OCIServer; + friend class BindableVarray; + friend class BindableVarrayNumber; + friend class BindableVarrayString; + }; + +} + +// ====================================================================== + +inline bool DB::OCISession::isOkToFetch() const +{ + return m_okToFetch; +} + +// ====================================================================== + +#endif +