From: Rafal Krypa Date: Mon, 2 Jun 2014 17:00:44 +0000 (+0200) Subject: DPL: import DPL::SqlConnection and dependencies X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e298b4337265d5e16c7b6fb7856a0e9c48199791;p=platform%2Fcore%2Fsecurity%2Fsecurity-manager.git DPL: import DPL::SqlConnection and dependencies Import SQLite wrapper class from wrt-commons 0.2.210. Several dependencies from DPL common imported as needed. Dependency DPL::Thread has been omitted on purpose, because it caused too many other dependencies. It will be substituded in a separate patch. These classes will be used by security-manager. When security-manager gets its own repository, SqlConnection will be removed from security-server. Change-Id: I090f73d6912f4ef6b85b313e7b12d20a7fd758a1 Signed-off-by: Rafal Krypa --- diff --git a/src/server/dpl/core/include/dpl/availability.h b/src/server/dpl/core/include/dpl/availability.h new file mode 100644 index 00000000..08138920 --- /dev/null +++ b/src/server/dpl/core/include/dpl/availability.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file availability.h + * @author Jihoon Chung (jihoon.chung@samsung.com) + * @version 1.0 + */ +#ifndef DPL_AVAILABILITY_H +#define DPL_AVAILABILITY_H + +#define DPL_DEPRECATED __attribute__((deprecated)) +#define DPL_DEPRECATED_WITH_MESSAGE(msg) __attribute__((deprecated(msg))) + +#define DPL_UNUSED __attribute__((unused)) +#define DPL_UNUSED_PARAM(variable) (void)variable + +#endif // DPL_AVAILABILITY_H diff --git a/src/server/dpl/core/include/dpl/char_traits.h b/src/server/dpl/core/include/dpl/char_traits.h new file mode 100644 index 00000000..eb2988ff --- /dev/null +++ b/src/server/dpl/core/include/dpl/char_traits.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file char_traits.h + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com) + * @version 1.0 + * @brief Char traits are used to create basic_string extended with + * additional features + * Current char traits could be extended in feature to boost + * performance + */ +#ifndef DPL_CHAR_TRAITS +#define DPL_CHAR_TRAITS + +#include +#include +#include +#include +#include + +namespace DPL { +typedef std::char_traits CharTraits; +} // namespace DPL + +#endif // DPL_CHAR_TRAITS diff --git a/src/server/dpl/core/include/dpl/errno_string.h b/src/server/dpl/core/include/dpl/errno_string.h new file mode 100644 index 00000000..446dbc9c --- /dev/null +++ b/src/server/dpl/core/include/dpl/errno_string.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file errno_string.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of errno string + */ +#ifndef DPL_ERRNO_STRING_H +#define DPL_ERRNO_STRING_H + +#include +#include +#include + +namespace DPL { +DECLARE_EXCEPTION_TYPE(DPL::Exception, InvalidErrnoValue) + +std::string GetErrnoString(int error = errno); +} // namespace DPL + +#endif // DPL_ERRNO_STRING_H diff --git a/src/server/dpl/core/include/dpl/free_deleter.h b/src/server/dpl/core/include/dpl/free_deleter.h new file mode 100644 index 00000000..4a09904d --- /dev/null +++ b/src/server/dpl/core/include/dpl/free_deleter.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file free_deleter.h + * @author Pawel Czajkowski (p.czajkowski@samsung.com) + * @version 1.0 + * @brief This file is the implementation file deleter with use std::free() + */ +#ifndef FREE_DELETER_H +#define FREE_DELETER_H + +#include +namespace DPL +{ +struct free_deleter +{ + void operator()(void *p) { std::free(p); } +}; +}// DPL +#endif // FREE_DELETER_H diff --git a/src/server/dpl/core/include/dpl/string.h b/src/server/dpl/core/include/dpl/string.h new file mode 100644 index 00000000..e4dc9231 --- /dev/null +++ b/src/server/dpl/core/include/dpl/string.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file string.h + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com) + * @version 1.0 + */ +#ifndef DPL_STRING +#define DPL_STRING + +#include +#include +#include +#include +#include + +namespace DPL { +// @brief DPL string +typedef std::basic_string String; + +// @brief String exception class +class StringException +{ + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + + // @brief Invalid init for UTF8 to UTF32 converter + DECLARE_EXCEPTION_TYPE(Base, IconvInitErrorUTF8ToUTF32) + + // @brief Invalid taStdContainerinit for UTF32 to UTF32 converter + DECLARE_EXCEPTION_TYPE(Base, IconvInitErrorUTF32ToUTF8) + + // @brief Invalid conversion for UTF8 to UTF32 converter + DECLARE_EXCEPTION_TYPE(Base, IconvConvertErrorUTF8ToUTF32) + + // @brief Invalid conversion for UTF8 to UTF32 converter + DECLARE_EXCEPTION_TYPE(Base, IconvConvertErrorUTF32ToUTF8) + + // @brief Invalid ASCII character detected in FromASCII + DECLARE_EXCEPTION_TYPE(Base, InvalidASCIICharacter) + + // @brief Invalid ASCII character detected in FromASCII + DECLARE_EXCEPTION_TYPE(Base, ICUInvalidCharacterFound) +}; + +//!\brief convert ASCII string to DPL::String +String FromASCIIString(const std::string& aString); + +//!\brief convert UTF32 string to DPL::String +String FromUTF32String(const std::wstring& aString); + +//@brief Returns String object created from UTF8 string +//@param[in] aString input UTF-8 string +String FromUTF8String(const std::string& aString); + +//@brief Returns String content as std::string +std::string ToUTF8String(const String& aString); + +//@brief Compare two unicode strings +int StringCompare(const String &left, + const String &right, + bool caseInsensitive = false); + +//@brief Splits the string into substrings. +//@param[in] str Input string +//@param[in] delimiters array or string containing a sequence of substring +// delimiters. Can be also a single delimiter character. +//@param[in] it InserterIterator that is used to save the generated substrings. +template +void Tokenize(const StringType& str, + const Delimiters& delimiters, + InserterIterator it, + bool ignoreEmpty = false) +{ + typename StringType::size_type nextSearchStart = 0; + typename StringType::size_type pos; + typename StringType::size_type length; + + while (true) { + pos = str.find_first_of(delimiters, nextSearchStart); + length = + ((pos == StringType::npos) ? str.length() : pos) - nextSearchStart; + + if (!ignoreEmpty || length > 0) { + *it = str.substr(nextSearchStart, length); + it++; + } + + if (pos == StringType::npos) { + return; + } + + nextSearchStart = pos + 1; + } +} + +namespace Utils { + +template class ConcatFunc : public std::binary_function +{ +public: + explicit ConcatFunc(const T & val) : m_delim(val) {} + T operator()(const T & arg1, const T & arg2) const + { + return arg1 + m_delim + arg2; + } +private: + T m_delim; +}; + +} + +template +typename ForwardIterator::value_type Join(ForwardIterator begin, ForwardIterator end, typename ForwardIterator::value_type delim) +{ + typedef typename ForwardIterator::value_type value; + if(begin == end) return value(); + Utils::ConcatFunc func(delim); + ForwardIterator init = begin; + return std::accumulate(++begin, end, *init, func); +} + +template void TrimLeft(StringType & obj, typename StringType::const_pointer separators) +{ + obj.erase(0, obj.find_first_not_of(separators)); +} + +template void TrimRight(StringType & obj, typename StringType::const_pointer separators) +{ + obj.erase(obj.find_last_not_of(separators)+1); +} + +template void Trim(StringType & obj, typename StringType::const_pointer separators) +{ + TrimLeft(obj, separators); + TrimRight(obj, separators); +} + + +} //namespace DPL + +std::ostream& operator<<(std::ostream& aStream, const DPL::String& aString); + +#endif // DPL_STRING diff --git a/src/server/dpl/core/src/errno_string.cpp b/src/server/dpl/core/src/errno_string.cpp new file mode 100644 index 00000000..6c597760 --- /dev/null +++ b/src/server/dpl/core/src/errno_string.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file errno_string.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of errno string + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL { +namespace // anonymous +{ +const size_t DEFAULT_ERRNO_STRING_SIZE = 32; +} // namespace anonymous + +std::string GetErrnoString(int error) +{ + size_t size = DEFAULT_ERRNO_STRING_SIZE; + char *buffer = NULL; + + for (;;) { + // Add one extra characted for end of string null value + char *newBuffer = static_cast(::realloc(buffer, size + 1)); + + if (!newBuffer) { + // Failed to realloc + ::free(buffer); + throw std::bad_alloc(); + } + + // Setup reallocated buffer + buffer = newBuffer; + ::memset(buffer, 0, size + 1); + + // Try to retrieve error string +#if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE + // The XSI-compliant version of strerror_r() is provided if: + int result = ::strerror_r(error, buffer, size); + + if (result == 0) { + std::unique_ptr scopedBufferFree(buffer); + return std::string(buffer); + } +#else + errno = 0; + + // Otherwise, the GNU-specific version is provided. + char *result = ::strerror_r(error, buffer, size); + + if (result != NULL) { + std::unique_ptr scopedBufferFree(buffer); + return std::string(result); + } +#endif + + // Interpret errors + switch (errno) { + case EINVAL: + // We got an invalid errno value + ::free(buffer); + ThrowMsg(InvalidErrnoValue, "Invalid errno value: " << error); + + case ERANGE: + // Incease buffer size and retry + size <<= 1; + continue; + + default: + AssertMsg(0, "Invalid errno value after call to strerror_r!"); + } + } +} +} // namespace DPL diff --git a/src/server/dpl/core/src/string.cpp b/src/server/dpl/core/src/string.cpp new file mode 100644 index 00000000..a642eaa2 --- /dev/null +++ b/src/server/dpl/core/src/string.cpp @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file string.cpp + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com) + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// TODO: Completely move to ICU +namespace DPL { +namespace //anonymous +{ +class ASCIIValidator +{ + const std::string& m_TestedString; + + public: + ASCIIValidator(const std::string& aTestedString); + + void operator()(char aCharacter) const; +}; + +ASCIIValidator::ASCIIValidator(const std::string& aTestedString) : + m_TestedString(aTestedString) +{} + +void ASCIIValidator::operator()(char aCharacter) const +{ + // Check for ASCII data range + if (aCharacter <= 0) { + ThrowMsg( + StringException::InvalidASCIICharacter, + "invalid character code " << static_cast(aCharacter) + << " from string [" << + m_TestedString + << "] passed as ASCII"); + } +} + +const iconv_t gc_IconvOperError = reinterpret_cast(-1); +const size_t gc_IconvConvertError = static_cast(-1); +} // namespace anonymous + +String FromUTF8String(const std::string& aIn) +{ + if (aIn.empty()) { + return String(); + } + + size_t inbytes = aIn.size(); + + // Default iconv UTF-32 module adds BOM (4 bytes) in from of string + // The worst case is when 8bit UTF-8 char converts to 32bit UTF-32 + // newsize = oldsize * 4 + end + bom + // newsize - bytes for UTF-32 string + // oldsize - letters in UTF-8 string + // end - end character for UTF-32 (\0) + // bom - Unicode header in front of string (0xfeff) + size_t outbytes = sizeof(wchar_t) * (inbytes + 2); + std::vector output(inbytes + 2, 0); + + size_t outbytesleft = outbytes; + char* inbuf = const_cast(aIn.c_str()); + + // vector is used to provide buffer for iconv which expects char* buffer + // but during conversion from UTF32 uses internaly wchar_t + char* outbuf = reinterpret_cast(&output[0]); + + iconv_t iconvHandle = iconv_open("UTF-32", "UTF-8"); + + if (gc_IconvOperError == iconvHandle) { + int error = errno; + + ThrowMsg(StringException::IconvInitErrorUTF8ToUTF32, + "iconv_open failed for " << "UTF-32 <- UTF-8" << + "error: " << GetErrnoString(error)); + } + + size_t iconvRet = iconv(iconvHandle, + &inbuf, + &inbytes, + &outbuf, + &outbytesleft); + + iconv_close(iconvHandle); + + if (gc_IconvConvertError == iconvRet) { + ThrowMsg(StringException::IconvConvertErrorUTF8ToUTF32, + "iconv failed for " << "UTF-32 <- UTF-8" << "error: " + << GetErrnoString()); + } + + // Ignore BOM in front of UTF-32 + return &output[1]; +} + +std::string ToUTF8String(const DPL::String& aIn) +{ + if (aIn.empty()) { + return std::string(); + } + + size_t inbytes = aIn.size() * sizeof(wchar_t); + size_t outbytes = inbytes + sizeof(char); + + // wstring returns wchar_t but iconv expects char* + // iconv internally is processing input as wchar_t + char* inbuf = reinterpret_cast(const_cast(aIn.c_str())); + std::vector output(inbytes, 0); + char* outbuf = &output[0]; + + size_t outbytesleft = outbytes; + + iconv_t iconvHandle = iconv_open("UTF-8", "UTF-32"); + + if (gc_IconvOperError == iconvHandle) { + ThrowMsg(StringException::IconvInitErrorUTF32ToUTF8, + "iconv_open failed for " << "UTF-8 <- UTF-32" + << "error: " << GetErrnoString()); + } + + size_t iconvRet = iconv(iconvHandle, + &inbuf, + &inbytes, + &outbuf, + &outbytesleft); + + iconv_close(iconvHandle); + + if (gc_IconvConvertError == iconvRet) { + ThrowMsg(StringException::IconvConvertErrorUTF32ToUTF8, + "iconv failed for " << "UTF-8 <- UTF-32" + << "error: " << GetErrnoString()); + } + + return &output[0]; +} + +String FromASCIIString(const std::string& aString) +{ + String output; + + std::for_each(aString.begin(), aString.end(), ASCIIValidator(aString)); + std::copy(aString.begin(), aString.end(), std::back_inserter(output)); + + return output; +} + +String FromUTF32String(const std::wstring& aString) +{ + return String(&aString[0]); +} + +static UChar *ConvertToICU(const String &inputString) +{ + std::unique_ptr outputString; + int32_t size = 0; + int32_t convertedSize = 0; + UErrorCode error = U_ZERO_ERROR; + + // Calculate size of output string + ::u_strFromWCS(NULL, + 0, + &size, + inputString.c_str(), + -1, + &error); + + if (error == U_ZERO_ERROR || + error == U_BUFFER_OVERFLOW_ERROR) + { + // What buffer size is ok ? + LogPedantic("ICU: Output buffer size: " << size); + } else { + ThrowMsg(StringException::ICUInvalidCharacterFound, + "ICU: Failed to retrieve output string size. Error: " + << error); + } + + // Allocate proper buffer + outputString.reset(new UChar[size + 1]); + ::memset(outputString.get(), 0, sizeof(UChar) * (size + 1)); + + error = U_ZERO_ERROR; + + // Do conversion + ::u_strFromWCS(outputString.get(), + size + 1, + &convertedSize, + inputString.c_str(), + -1, + &error); + + if (!U_SUCCESS(error)) { + ThrowMsg(StringException::ICUInvalidCharacterFound, + "ICU: Failed to convert string. Error: " << error); + } + + // Done + return outputString.release(); +} + +int StringCompare(const String &left, + const String &right, + bool caseInsensitive) +{ + // Convert input strings + std::unique_ptr leftICU(ConvertToICU(left)); + std::unique_ptr rightICU(ConvertToICU(right)); + + if (caseInsensitive) { + return static_cast(u_strcasecmp(leftICU.get(), rightICU.get(), 0)); + } else { + return static_cast(u_strcmp(leftICU.get(), rightICU.get())); + } +} +} //namespace DPL + +std::ostream& operator<<(std::ostream& aStream, const DPL::String& aString) +{ + return aStream << DPL::ToUTF8String(aString); +} diff --git a/src/server/dpl/db/include/dpl/db/naive_synchronization_object.h b/src/server/dpl/db/include/dpl/db/naive_synchronization_object.h new file mode 100644 index 00000000..2f63a0f3 --- /dev/null +++ b/src/server/dpl/db/include/dpl/db/naive_synchronization_object.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file naive_synchronization_object.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of SQL naive + * synchronization object + */ +#ifndef DPL_NAIVE_SYNCHRONIZATION_OBJECT_H +#define DPL_NAIVE_SYNCHRONIZATION_OBJECT_H + +#include + +namespace DPL { +namespace DB { +/** + * Naive synchronization object used to synchronize SQL connection + * to the same database across different threads and processes + */ +class NaiveSynchronizationObject : + public SqlConnection::SynchronizationObject +{ + public: + // [SqlConnection::SynchronizationObject] + virtual void Synchronize(); + virtual void NotifyAll(); +}; +} // namespace DB +} // namespace DPL + +#endif // DPL_NAIVE_SYNCHRONIZATION_OBJECT_H diff --git a/src/server/dpl/db/include/dpl/db/sql_connection.h b/src/server/dpl/db/include/dpl/db/sql_connection.h new file mode 100644 index 00000000..18a5d3f9 --- /dev/null +++ b/src/server/dpl/db/include/dpl/db/sql_connection.h @@ -0,0 +1,513 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file sql_connection.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of SQL connection + */ +#ifndef DPL_SQL_CONNECTION_H +#define DPL_SQL_CONNECTION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL { +namespace DB { +/** + * SQL connection class + */ +class SqlConnection +{ + public: + /** + * SQL Exception classes + */ + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, SyntaxError) + DECLARE_EXCEPTION_TYPE(Base, ConnectionBroken) + DECLARE_EXCEPTION_TYPE(Base, InternalError) + DECLARE_EXCEPTION_TYPE(Base, InvalidColumn) + }; + + typedef int ColumnIndex; + typedef int ArgumentIndex; + + /* + * SQL processed data command + */ + class DataCommand : + private Noncopyable + { + private: + SqlConnection *m_masterConnection; + sqlite3_stmt *m_stmt; + + void CheckBindResult(int result); + void CheckColumnIndex(SqlConnection::ColumnIndex column); + + DataCommand(SqlConnection *connection, const char *buffer); + + friend class SqlConnection; + + public: + virtual ~DataCommand(); + + /** + * Bind null to the prepared statement argument + * + * @param position Index of argument to bind value to + */ + void BindNull(ArgumentIndex position); + + /** + * Bind int to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInteger(ArgumentIndex position, int value); + + /** + * Bind int8_t to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInt8(ArgumentIndex position, int8_t value); + + /** + * Bind int16 to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInt16(ArgumentIndex position, int16_t value); + + /** + * Bind int32 to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInt32(ArgumentIndex position, int32_t value); + + /** + * Bind int64 to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInt64(ArgumentIndex position, int64_t value); + + /** + * Bind float to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindFloat(ArgumentIndex position, float value); + + /** + * Bind double to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindDouble(ArgumentIndex position, double value); + + /** + * Bind string to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindString(ArgumentIndex position, const char *value); + + /** + * Bind string to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindString(ArgumentIndex position, const String& value); + + /** + * Bind optional int to the prepared statement argument. + * If optional is not set null will be bound + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInteger(ArgumentIndex position, const boost::optional &value); + + /** + * Bind optional int8 to the prepared statement argument. + * If optional is not set null will be bound + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInt8(ArgumentIndex position, const boost::optional &value); + + /** + * Bind optional int16 to the prepared statement argument. + * If optional is not set null will be bound + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInt16(ArgumentIndex position, const boost::optional &value); + + /** + * Bind optional int32 to the prepared statement argument. + * If optional is not set null will be bound + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInt32(ArgumentIndex position, const boost::optional &value); + + /** + * Bind optional int64 to the prepared statement argument. + * If optional is not set null will be bound + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInt64(ArgumentIndex position, const boost::optional &value); + + /** + * Bind optional float to the prepared statement argument. + * If optional is not set null will be bound + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindFloat(ArgumentIndex position, const boost::optional &value); + + /** + * Bind optional double to the prepared statement argument. + * If optional is not set null will be bound + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindDouble(ArgumentIndex position, const boost::optional &value); + + /** + * Bind optional string to the prepared statement argument. + * If optional is not set null will be bound + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindString(ArgumentIndex position, const boost::optional &value); + + /** + * Execute the prepared statement and/or move + * to the next row of the result + * + * @return True when there was a row returned + */ + bool Step(); + + /** + * Reset prepared statement's arguments + * All parameters will become null + */ + void Reset(); + + /** + * Checks whether column value is null + * + * @throw Exception::InvalidColumn + */ + bool IsColumnNull(ColumnIndex column); + + /** + * Get integer value from column in current row. + * + * @throw Exception::InvalidColumn + */ + int GetColumnInteger(ColumnIndex column); + + /** + * Get int8 value from column in current row. + * + * @throw Exception::InvalidColumn + */ + int8_t GetColumnInt8(ColumnIndex column); + + /** + * Get int16 value from column in current row. + * + * @throw Exception::InvalidColumn + */ + int16_t GetColumnInt16(ColumnIndex column); + /** + * Get int32 value from column in current row. + * + * @throw Exception::InvalidColumn + */ + int32_t GetColumnInt32(ColumnIndex column); + + /** + * Get int64 value from column in current row. + * + * @throw Exception::InvalidColumn + */ + int64_t GetColumnInt64(ColumnIndex column); + + /** + * Get float value from column in current row. + * + * @throw Exception::InvalidColumn + */ + float GetColumnFloat(ColumnIndex column); + + /** + * Get double value from column in current row. + * + * @throw Exception::InvalidColumn + */ + double GetColumnDouble(ColumnIndex column); + + /** + * Get string value from column in current row. + * + * @throw Exception::InvalidColumn + */ + std::string GetColumnString(ColumnIndex column); + + /** + * Get optional integer value from column in current row. + * + * @throw Exception::InvalidColumn + */ + boost::optional GetColumnOptionalInteger(ColumnIndex column); + + /** + * Get optional int8 value from column in current row. + * + * @throw Exception::InvalidColumn + */ + boost::optional GetColumnOptionalInt8(ColumnIndex column); + + /** + * Get optional int16value from column in current row. + * + * @throw Exception::InvalidColumn + */ + boost::optional GetColumnOptionalInt16(ColumnIndex column); + + /** + * Get optional int32 value from column in current row. + * + * @throw Exception::InvalidColumn + */ + boost::optional GetColumnOptionalInt32(ColumnIndex column); + + /** + * Get optional int64 value from column in current row. + * + * @throw Exception::InvalidColumn + */ + boost::optional GetColumnOptionalInt64(ColumnIndex column); + + /** + * Get optional float value from column in current row. + * + * @throw Exception::InvalidColumn + */ + boost::optional GetColumnOptionalFloat(ColumnIndex column); + + /** + * Get optional double value from column in current row. + * + * @throw Exception::InvalidColumn + */ + boost::optional GetColumnOptionalDouble(ColumnIndex column); + + /** + * Get optional string value from column in current row. + * + * @throw Exception::InvalidColumn + */ + boost::optional GetColumnOptionalString(ColumnIndex column); + }; + + // Move on copy semantics + typedef std::auto_ptr DataCommandAutoPtr; + + // Open flags + class Flag + { + public: + enum Type + { + None = 1 << 0, + UseLucene = 1 << 1 + }; + + enum Option + { + RO = SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_READONLY, + /** + * *TODO: please remove CREATE option from RW flag when all places + * that need that switched do CRW + */ + RW = SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_READWRITE | + SQLITE_OPEN_CREATE, + CRW = RW | SQLITE_OPEN_CREATE + }; + }; + + // RowID + typedef sqlite3_int64 RowID; + + /** + * Synchronization object used to synchronize SQL connection + * to the same database across different threads and processes + */ + class SynchronizationObject + { + public: + virtual ~SynchronizationObject() {} + + /** + * Synchronizes SQL connection for multiple clients. + */ + virtual void Synchronize() = 0; + + /** + * Notify all waiting clients that the connection is no longer locked. + */ + virtual void NotifyAll() = 0; + }; + + protected: + sqlite3 *m_connection; + + // Options + bool m_usingLucene; + + // Stored data procedures + int m_dataCommandsCount; + + // Synchronization object + std::unique_ptr m_synchronizationObject; + + virtual void Connect(const std::string &address, + Flag::Type = Flag::None, Flag::Option = Flag::RO); + virtual void Disconnect(); + + void TurnOnForeignKeys(); + + static SynchronizationObject *AllocDefaultSynchronizationObject(); + + public: + /** + * Open SQL connection + * + * Synchronization is archieved by using provided asynchronization object. + * If synchronizationObject is set to NULL, so synchronization is performed. + * Ownership of the synchronization object is transfered to sql connection + * object. + * + * @param address Database file name + * @param flags Open flags + * @param synchronizationObject A synchronization object to use. + */ + explicit SqlConnection(const std::string &address = std::string(), + Flag::Type flags = Flag::None, + Flag::Option options = Flag::RO, + SynchronizationObject *synchronizationObject = + AllocDefaultSynchronizationObject()); + + /** + * Destructor + */ + virtual ~SqlConnection(); + + /** + * Execute SQL command without result + * + * @param format + * @param ... + */ + void ExecCommand(const char *format, ...) DPL_DEPRECATED_WITH_MESSAGE( + "To prevent sql injection do not use this \ + method for direct sql execution"); + + /** + * Execute BEGIN; command to start new transaction + * + */ + void BeginTransaction(); + + /** + * Execute ROLLBACK; command to discard changes made + * + */ + void RollbackTransaction(); + + /** + * Execute COMMIT; command to commit changes in database + * + */ + void CommitTransaction(); + + /** + * Prepare stored procedure + * + * @param format SQL statement + * @return Data command representing stored procedure + */ + DataCommandAutoPtr PrepareDataCommand(const char *format, ...); + + /** + * Check whether given table exists + * + * @param tableName Name of the table to check + * @return True if given table name exists + */ + bool CheckTableExist(const char *tableName); + + /** + * Get last insert operation new row id + * + * @return Row ID + */ + RowID GetLastInsertRowID() const; +}; +} // namespace DB +} // namespace DPL + +#endif // DPL_SQL_CONNECTION_H diff --git a/src/server/dpl/db/src/naive_synchronization_object.cpp b/src/server/dpl/db/src/naive_synchronization_object.cpp new file mode 100644 index 00000000..1ac71ca4 --- /dev/null +++ b/src/server/dpl/db/src/naive_synchronization_object.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file naive_synchronization_object.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of SQL naive + * synchronization object + */ +#include +#include +#include + +namespace { + unsigned int seed = time(NULL); +} + +namespace DPL { +namespace DB { +void NaiveSynchronizationObject::Synchronize() +{ + // Sleep for about 10ms - 30ms + Thread::MiliSleep(10 + rand_r(&seed) % 20); +} + +void NaiveSynchronizationObject::NotifyAll() +{ + // No need to inform about anything +} +} // namespace DB +} // namespace DPL diff --git a/src/server/dpl/db/src/sql_connection.cpp b/src/server/dpl/db/src/sql_connection.cpp new file mode 100644 index 00000000..57ea9b9b --- /dev/null +++ b/src/server/dpl/db/src/sql_connection.cpp @@ -0,0 +1,866 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file sql_connection.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of SQL connection + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL { +namespace DB { +namespace // anonymous +{ +class ScopedNotifyAll : + public Noncopyable +{ + private: + SqlConnection::SynchronizationObject *m_synchronizationObject; + + public: + explicit ScopedNotifyAll( + SqlConnection::SynchronizationObject *synchronizationObject) : + m_synchronizationObject(synchronizationObject) + {} + + ~ScopedNotifyAll() + { + if (!m_synchronizationObject) { + return; + } + + LogPedantic("Notifying after successful synchronize"); + m_synchronizationObject->NotifyAll(); + } +}; +} // namespace anonymous + +SqlConnection::DataCommand::DataCommand(SqlConnection *connection, + const char *buffer) : + m_masterConnection(connection), + m_stmt(NULL) +{ + Assert(connection != NULL); + + // Notify all after potentially synchronized database connection access + ScopedNotifyAll notifyAll(connection->m_synchronizationObject.get()); + + for (;;) { + int ret = sqlite3_prepare_v2(connection->m_connection, + buffer, strlen(buffer), + &m_stmt, NULL); + + if (ret == SQLITE_OK) { + LogPedantic("Data command prepared successfuly"); + break; + } else if (ret == SQLITE_BUSY) { + LogPedantic("Collision occurred while preparing SQL command"); + + // Synchronize if synchronization object is available + if (connection->m_synchronizationObject) { + LogPedantic("Performing synchronization"); + connection->m_synchronizationObject->Synchronize(); + continue; + } + + // No synchronization object defined. Fail. + } + + // Fatal error + const char *error = sqlite3_errmsg(m_masterConnection->m_connection); + + LogPedantic("SQL prepare data command failed"); + LogPedantic(" Statement: " << buffer); + LogPedantic(" Error: " << error); + + ThrowMsg(Exception::SyntaxError, error); + } + + LogPedantic("Prepared data command: " << buffer); + + // Increment stored data command count + ++m_masterConnection->m_dataCommandsCount; +} + +SqlConnection::DataCommand::~DataCommand() +{ + LogPedantic("SQL data command finalizing"); + + if (sqlite3_finalize(m_stmt) != SQLITE_OK) { + LogPedantic("Failed to finalize data command"); + } + + // Decrement stored data command count + --m_masterConnection->m_dataCommandsCount; +} + +void SqlConnection::DataCommand::CheckBindResult(int result) +{ + if (result != SQLITE_OK) { + const char *error = sqlite3_errmsg( + m_masterConnection->m_connection); + + LogPedantic("Failed to bind SQL statement parameter"); + LogPedantic(" Error: " << error); + + ThrowMsg(Exception::SyntaxError, error); + } +} + +void SqlConnection::DataCommand::BindNull( + SqlConnection::ArgumentIndex position) +{ + CheckBindResult(sqlite3_bind_null(m_stmt, position)); + LogPedantic("SQL data command bind null: [" + << position << "]"); +} + +void SqlConnection::DataCommand::BindInteger( + SqlConnection::ArgumentIndex position, + int value) +{ + CheckBindResult(sqlite3_bind_int(m_stmt, position, value)); + LogPedantic("SQL data command bind integer: [" + << position << "] -> " << value); +} + +void SqlConnection::DataCommand::BindInt8( + SqlConnection::ArgumentIndex position, + int8_t value) +{ + CheckBindResult(sqlite3_bind_int(m_stmt, position, + static_cast(value))); + LogPedantic("SQL data command bind int8: [" + << position << "] -> " << value); +} + +void SqlConnection::DataCommand::BindInt16( + SqlConnection::ArgumentIndex position, + int16_t value) +{ + CheckBindResult(sqlite3_bind_int(m_stmt, position, + static_cast(value))); + LogPedantic("SQL data command bind int16: [" + << position << "] -> " << value); +} + +void SqlConnection::DataCommand::BindInt32( + SqlConnection::ArgumentIndex position, + int32_t value) +{ + CheckBindResult(sqlite3_bind_int(m_stmt, position, + static_cast(value))); + LogPedantic("SQL data command bind int32: [" + << position << "] -> " << value); +} + +void SqlConnection::DataCommand::BindInt64( + SqlConnection::ArgumentIndex position, + int64_t value) +{ + CheckBindResult(sqlite3_bind_int64(m_stmt, position, + static_cast(value))); + LogPedantic("SQL data command bind int64: [" + << position << "] -> " << value); +} + +void SqlConnection::DataCommand::BindFloat( + SqlConnection::ArgumentIndex position, + float value) +{ + CheckBindResult(sqlite3_bind_double(m_stmt, position, + static_cast(value))); + LogPedantic("SQL data command bind float: [" + << position << "] -> " << value); +} + +void SqlConnection::DataCommand::BindDouble( + SqlConnection::ArgumentIndex position, + double value) +{ + CheckBindResult(sqlite3_bind_double(m_stmt, position, value)); + LogPedantic("SQL data command bind double: [" + << position << "] -> " << value); +} + +void SqlConnection::DataCommand::BindString( + SqlConnection::ArgumentIndex position, + const char *value) +{ + if (!value) { + BindNull(position); + return; + } + + // Assume that text may disappear + CheckBindResult(sqlite3_bind_text(m_stmt, position, + value, strlen(value), + SQLITE_TRANSIENT)); + + LogPedantic("SQL data command bind string: [" + << position << "] -> " << value); +} + +void SqlConnection::DataCommand::BindString( + SqlConnection::ArgumentIndex position, + const String &value) +{ + BindString(position, ToUTF8String(value).c_str()); +} + +void SqlConnection::DataCommand::BindInteger( + SqlConnection::ArgumentIndex position, + const boost::optional &value) +{ + if (!value) { + BindNull(position); + } else { + BindInteger(position, *value); + } +} + +void SqlConnection::DataCommand::BindInt8( + SqlConnection::ArgumentIndex position, + const boost::optional &value) +{ + if (!value) { + BindNull(position); + } else { + BindInt8(position, *value); + } +} + +void SqlConnection::DataCommand::BindInt16( + SqlConnection::ArgumentIndex position, + const boost::optional &value) +{ + if (!value) { + BindNull(position); + } else { + BindInt16(position, *value); + } +} + +void SqlConnection::DataCommand::BindInt32( + SqlConnection::ArgumentIndex position, + const boost::optional &value) +{ + if (!value) { + BindNull(position); + } else { + BindInt32(position, *value); + } +} + +void SqlConnection::DataCommand::BindInt64( + SqlConnection::ArgumentIndex position, + const boost::optional &value) +{ + if (!value) { + BindNull(position); + } else { + BindInt64(position, *value); + } +} + +void SqlConnection::DataCommand::BindFloat( + SqlConnection::ArgumentIndex position, + const boost::optional &value) +{ + if (!value) { + BindNull(position); + } else { + BindFloat(position, *value); + } +} + +void SqlConnection::DataCommand::BindDouble( + SqlConnection::ArgumentIndex position, + const boost::optional &value) +{ + if (!value) { + BindNull(position); + } else { + BindDouble(position, *value); + } +} + +void SqlConnection::DataCommand::BindString( + SqlConnection::ArgumentIndex position, + const boost::optional &value) +{ + if (!!value) { + BindString(position, ToUTF8String(*value).c_str()); + } else { + BindNull(position); + } +} + +bool SqlConnection::DataCommand::Step() +{ + // Notify all after potentially synchronized database connection access + ScopedNotifyAll notifyAll( + m_masterConnection->m_synchronizationObject.get()); + + for (;;) { + int ret = sqlite3_step(m_stmt); + + if (ret == SQLITE_ROW) { + LogPedantic("SQL data command step ROW"); + return true; + } else if (ret == SQLITE_DONE) { + LogPedantic("SQL data command step DONE"); + return false; + } else if (ret == SQLITE_BUSY) { + LogPedantic("Collision occurred while executing SQL command"); + + // Synchronize if synchronization object is available + if (m_masterConnection->m_synchronizationObject) { + LogPedantic("Performing synchronization"); + + m_masterConnection-> + m_synchronizationObject->Synchronize(); + + continue; + } + + // No synchronization object defined. Fail. + } + + // Fatal error + const char *error = sqlite3_errmsg(m_masterConnection->m_connection); + + LogPedantic("SQL step data command failed"); + LogPedantic(" Error: " << error); + + ThrowMsg(Exception::InternalError, error); + } +} + +void SqlConnection::DataCommand::Reset() +{ + /* + * According to: + * http://www.sqlite.org/c3ref/stmt.html + * + * if last sqlite3_step command on this stmt returned an error, + * then sqlite3_reset will return that error, althought it is not an error. + * So sqlite3_reset allways succedes. + */ + sqlite3_reset(m_stmt); + + LogPedantic("SQL data command reset"); +} + +void SqlConnection::DataCommand::CheckColumnIndex( + SqlConnection::ColumnIndex column) +{ + if (column < 0 || column >= sqlite3_column_count(m_stmt)) { + ThrowMsg(Exception::InvalidColumn, "Column index is out of bounds"); + } +} + +bool SqlConnection::DataCommand::IsColumnNull( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column type: [" << column << "]"); + CheckColumnIndex(column); + return sqlite3_column_type(m_stmt, column) == SQLITE_NULL; +} + +int SqlConnection::DataCommand::GetColumnInteger( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column integer: [" << column << "]"); + CheckColumnIndex(column); + int value = sqlite3_column_int(m_stmt, column); + LogPedantic(" Value: " << value); + return value; +} + +int8_t SqlConnection::DataCommand::GetColumnInt8( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column int8: [" << column << "]"); + CheckColumnIndex(column); + int8_t value = static_cast(sqlite3_column_int(m_stmt, column)); + LogPedantic(" Value: " << value); + return value; +} + +int16_t SqlConnection::DataCommand::GetColumnInt16( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column int16: [" << column << "]"); + CheckColumnIndex(column); + int16_t value = static_cast(sqlite3_column_int(m_stmt, column)); + LogPedantic(" Value: " << value); + return value; +} + +int32_t SqlConnection::DataCommand::GetColumnInt32( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column int32: [" << column << "]"); + CheckColumnIndex(column); + int32_t value = static_cast(sqlite3_column_int(m_stmt, column)); + LogPedantic(" Value: " << value); + return value; +} + +int64_t SqlConnection::DataCommand::GetColumnInt64( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column int64: [" << column << "]"); + CheckColumnIndex(column); + int64_t value = static_cast(sqlite3_column_int64(m_stmt, column)); + LogPedantic(" Value: " << value); + return value; +} + +float SqlConnection::DataCommand::GetColumnFloat( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column float: [" << column << "]"); + CheckColumnIndex(column); + float value = static_cast(sqlite3_column_double(m_stmt, column)); + LogPedantic(" Value: " << value); + return value; +} + +double SqlConnection::DataCommand::GetColumnDouble( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column double: [" << column << "]"); + CheckColumnIndex(column); + double value = sqlite3_column_double(m_stmt, column); + LogPedantic(" Value: " << value); + return value; +} + +std::string SqlConnection::DataCommand::GetColumnString( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column string: [" << column << "]"); + CheckColumnIndex(column); + + const char *value = reinterpret_cast( + sqlite3_column_text(m_stmt, column)); + + LogPedantic("Value: " << (value ? value : "NULL")); + + if (value == NULL) { + return std::string(); + } + + return std::string(value); +} + +boost::optional SqlConnection::DataCommand::GetColumnOptionalInteger( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column optional integer: [" + << column << "]"); + CheckColumnIndex(column); + if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL) { + return boost::optional(); + } + int value = sqlite3_column_int(m_stmt, column); + LogPedantic(" Value: " << value); + return boost::optional(value); +} + +boost::optional SqlConnection::DataCommand::GetColumnOptionalInt8( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column optional int8: [" + << column << "]"); + CheckColumnIndex(column); + if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL) { + return boost::optional(); + } + int8_t value = static_cast(sqlite3_column_int(m_stmt, column)); + LogPedantic(" Value: " << value); + return boost::optional(value); +} + +boost::optional SqlConnection::DataCommand::GetColumnOptionalInt16( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column optional int16: [" + << column << "]"); + CheckColumnIndex(column); + if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL) { + return boost::optional(); + } + int16_t value = static_cast(sqlite3_column_int(m_stmt, column)); + LogPedantic(" Value: " << value); + return boost::optional(value); +} + +boost::optional SqlConnection::DataCommand::GetColumnOptionalInt32( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column optional int32: [" + << column << "]"); + CheckColumnIndex(column); + if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL) { + return boost::optional(); + } + int32_t value = static_cast(sqlite3_column_int(m_stmt, column)); + LogPedantic(" Value: " << value); + return boost::optional(value); +} + +boost::optional SqlConnection::DataCommand::GetColumnOptionalInt64( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column optional int64: [" + << column << "]"); + CheckColumnIndex(column); + if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL) { + return boost::optional(); + } + int64_t value = static_cast(sqlite3_column_int64(m_stmt, column)); + LogPedantic(" Value: " << value); + return boost::optional(value); +} + +boost::optional SqlConnection::DataCommand::GetColumnOptionalFloat( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column optional float: [" + << column << "]"); + CheckColumnIndex(column); + if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL) { + return boost::optional(); + } + float value = static_cast(sqlite3_column_double(m_stmt, column)); + LogPedantic(" Value: " << value); + return boost::optional(value); +} + +boost::optional SqlConnection::DataCommand::GetColumnOptionalDouble( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column optional double: [" + << column << "]"); + CheckColumnIndex(column); + if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL) { + return boost::optional(); + } + double value = sqlite3_column_double(m_stmt, column); + LogPedantic(" Value: " << value); + return boost::optional(value); +} + +boost::optional SqlConnection::DataCommand::GetColumnOptionalString( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column optional string: [" + << column << "]"); + CheckColumnIndex(column); + if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL) { + return boost::optional(); + } + const char *value = reinterpret_cast( + sqlite3_column_text(m_stmt, column)); + LogPedantic("Value: " << value); + String s = FromUTF8String(value); + return boost::optional(s); +} + +void SqlConnection::Connect(const std::string &address, + Flag::Type type, + Flag::Option flag) +{ + if (m_connection != NULL) { + LogPedantic("Already connected."); + return; + } + LogPedantic("Connecting to DB: " << address << "..."); + + // Connect to database + int result; + if (type & Flag::UseLucene) { + result = db_util_open_with_options( + address.c_str(), + &m_connection, + flag, + NULL); + + m_usingLucene = true; + LogPedantic("Lucene index enabled"); + } else { + result = sqlite3_open_v2( + address.c_str(), + &m_connection, + flag, + NULL); + + m_usingLucene = false; + LogPedantic("Lucene index disabled"); + } + + if (result == SQLITE_OK) { + LogPedantic("Connected to DB"); + } else { + LogPedantic("Failed to connect to DB!"); + ThrowMsg(Exception::ConnectionBroken, address); + } + + // Enable foreign keys + TurnOnForeignKeys(); +} + +void SqlConnection::Disconnect() +{ + if (m_connection == NULL) { + LogPedantic("Already disconnected."); + return; + } + + LogPedantic("Disconnecting from DB..."); + + // All stored data commands must be deleted before disconnect + AssertMsg(m_dataCommandsCount == 0, + "All stored procedures must be deleted" + " before disconnecting SqlConnection"); + + int result; + + if (m_usingLucene) { + result = db_util_close(m_connection); + } else { + result = sqlite3_close(m_connection); + } + + if (result != SQLITE_OK) { + const char *error = sqlite3_errmsg(m_connection); + LogPedantic("SQL close failed"); + LogPedantic(" Error: " << error); + Throw(Exception::InternalError); + } + + m_connection = NULL; + + LogPedantic("Disconnected from DB"); +} + +bool SqlConnection::CheckTableExist(const char *tableName) +{ + if (m_connection == NULL) { + LogPedantic("Cannot execute command. Not connected to DB!"); + return false; + } + + DataCommandAutoPtr command = + PrepareDataCommand("select tbl_name from sqlite_master where name=?;"); + + command->BindString(1, tableName); + + if (!command->Step()) { + LogPedantic("No matching records in table"); + return false; + } + + return command->GetColumnString(0) == tableName; +} + +SqlConnection::SqlConnection(const std::string &address, + Flag::Type flag, + Flag::Option option, + SynchronizationObject *synchronizationObject) : + m_connection(NULL), + m_usingLucene(false), + m_dataCommandsCount(0), + m_synchronizationObject(synchronizationObject) +{ + LogPedantic("Opening database connection to: " << address); + + // Connect to DB + SqlConnection::Connect(address, flag, option); + + if (!m_synchronizationObject) { + LogPedantic("No synchronization object defined"); + } +} + +SqlConnection::~SqlConnection() +{ + LogPedantic("Closing database connection"); + + // Disconnect from DB + Try + { + SqlConnection::Disconnect(); + } + Catch(Exception::Base) + { + LogPedantic("Failed to disconnect from database"); + } +} + +void SqlConnection::ExecCommand(const char *format, ...) +{ + if (m_connection == NULL) { + LogPedantic("Cannot execute command. Not connected to DB!"); + return; + } + + if (format == NULL) { + LogPedantic("Null query!"); + ThrowMsg(Exception::SyntaxError, "Null statement"); + } + + char *rawBuffer; + + va_list args; + va_start(args, format); + + if (vasprintf(&rawBuffer, format, args) == -1) { + rawBuffer = NULL; + } + + va_end(args); + + std::unique_ptr buffer(rawBuffer); + + if (!buffer) { + LogPedantic("Failed to allocate statement string"); + return; + } + + LogPedantic("Executing SQL command: " << buffer.get()); + + // Notify all after potentially synchronized database connection access + ScopedNotifyAll notifyAll(m_synchronizationObject.get()); + + for (;;) { + char *errorBuffer; + + int ret = sqlite3_exec(m_connection, + buffer.get(), + NULL, + NULL, + &errorBuffer); + + std::string errorMsg; + + // Take allocated error buffer + if (errorBuffer != NULL) { + errorMsg = errorBuffer; + sqlite3_free(errorBuffer); + } + + if (ret == SQLITE_OK) { + return; + } + + if (ret == SQLITE_BUSY) { + LogPedantic("Collision occurred while executing SQL command"); + + // Synchronize if synchronization object is available + if (m_synchronizationObject) { + LogPedantic("Performing synchronization"); + m_synchronizationObject->Synchronize(); + continue; + } + + // No synchronization object defined. Fail. + } + + // Fatal error + LogPedantic("Failed to execute SQL command. Error: " << errorMsg); + ThrowMsg(Exception::SyntaxError, errorMsg); + } +} + +SqlConnection::DataCommandAutoPtr SqlConnection::PrepareDataCommand( + const char *format, + ...) +{ + if (m_connection == NULL) { + LogPedantic("Cannot execute data command. Not connected to DB!"); + return DataCommandAutoPtr(); + } + + char *rawBuffer; + + va_list args; + va_start(args, format); + + if (vasprintf(&rawBuffer, format, args) == -1) { + rawBuffer = NULL; + } + + va_end(args); + + std::unique_ptr buffer(rawBuffer); + + if (!buffer) { + LogPedantic("Failed to allocate statement string"); + return DataCommandAutoPtr(); + } + + LogPedantic("Executing SQL data command: " << buffer.get()); + + return DataCommandAutoPtr(new DataCommand(this, buffer.get())); +} + +SqlConnection::RowID SqlConnection::GetLastInsertRowID() const +{ + return static_cast(sqlite3_last_insert_rowid(m_connection)); +} + +void SqlConnection::TurnOnForeignKeys() +{ + ExecCommand("PRAGMA foreign_keys = ON;"); +} + +void SqlConnection::BeginTransaction() +{ + ExecCommand("BEGIN;"); +} + +void SqlConnection::RollbackTransaction() +{ + ExecCommand("ROLLBACK;"); +} + +void SqlConnection::CommitTransaction() +{ + ExecCommand("COMMIT;"); +} + +SqlConnection::SynchronizationObject * +SqlConnection::AllocDefaultSynchronizationObject() +{ + return new NaiveSynchronizationObject(); +} +} // namespace DB +} // namespace DPL