diff --git a/CMakeLists.txt b/CMakeLists.txt index 550bce73..2ae82716 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,25 +39,31 @@ option(SQLCIPHER_CONNECTOR "Build SQLite3 Connector with SQLCipher" OFF) if(MYSQL_CONNECTOR) find_package(MySQL REQUIRED) else() - message(STATUS "Not building MYSQL_CONNECTOR") + message(STATUS "Not building tests for MYSQL_CONNECTOR") endif() if(MARIADB_CONNECTOR) find_package(MariaDB REQUIRED) else() - message(STATUS "Not building MARIAB_CONNECTOR") + message(STATUS "Not building tests for MARIAB_CONNECTOR") +endif() + +if(POSTGRESQL_CONNECTOR) + find_package(PostgreSQL REQUIRED) +else() + message(STATUS "Not building tests for POSTGRESQL_CONNECTOR") endif() if(SQLITE3_CONNECTOR) find_package(SQLite3 REQUIRED) else() - message(STATUS "Not building SQLITE3_CONNECTOR") + message(STATUS "Not building tests for SQLITE3_CONNECTOR") endif() if(SQLCIPHER_CONNECTOR) find_package(SQLCipher REQUIRED) else() - message(STATUS "Not building SQLCIPHER_CONNECTOR") + message(STATUS "Not building tests for SQLCIPHER_CONNECTOR") endif() include(CTest) diff --git a/include/sqlpp11/detail/column_tuple_merge.h b/include/sqlpp11/detail/column_tuple_merge.h new file mode 100644 index 00000000..1851a030 --- /dev/null +++ b/include/sqlpp11/detail/column_tuple_merge.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021-2021, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP11_COLUMN_TUPLE_MERGE_H +#define SQLPP11_COLUMN_TUPLE_MERGE_H + +#include + +#include + +namespace sqlpp +{ + namespace detail + { + template + std::tuple> as_column_tuple(T t) + { + return std::tuple>(auto_alias_t{t}); + } + + template + std::tuple...> as_column_tuple(std::tuple t) + { + return t; + } + + template + auto column_tuple_merge(Columns... columns) -> decltype(std::tuple_cat(as_column_tuple(columns)...)) + { + return std::tuple_cat(as_column_tuple(columns)...); + } + } +} // namespace sqlpp + +#endif diff --git a/include/sqlpp11/postgresql/bind_result.h b/include/sqlpp11/postgresql/bind_result.h new file mode 100644 index 00000000..eaa52b61 --- /dev/null +++ b/include/sqlpp11/postgresql/bind_result.h @@ -0,0 +1,397 @@ +/** + * Copyright © 2014-2020, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_BIND_RESULT_H +#define SQLPP_POSTGRESQL_BIND_RESULT_H + +#include +#include +#include + +#include +#include +#include + +#include "detail/prepared_statement_handle.h" + +#if defined(_WIN32) || defined(_WIN64) +#pragma warning(disable : 4800) // int to bool +#endif +namespace sqlpp +{ + namespace postgresql + { + namespace detail + { + struct statement_handle_t; + } + + class bind_result_t + { + private: + std::shared_ptr _handle; + + bool next_impl(); + + public: + bind_result_t() = default; + bind_result_t(const std::shared_ptr& handle); + bind_result_t(const bind_result_t&) = delete; + bind_result_t(bind_result_t&&) = default; + bind_result_t& operator=(const bind_result_t&) = delete; + bind_result_t& operator=(bind_result_t&&) = default; + ~bind_result_t() = default; + + bool operator==(const bind_result_t& rhs) const + { + return (this->_handle == rhs._handle); + } + + template + void next(ResultRow& result_row) + { + if (!this->_handle) + { + result_row._invalidate(); + return; + } + + if (this->next_impl()) + { + if (!result_row) + { + result_row._validate(); + } + result_row._bind(*this); + } + else + { + if (result_row) + { + result_row._invalidate(); + } + } + } + + void _bind_boolean_result(size_t index, signed char* value, bool* is_null); + void _bind_floating_point_result(size_t index, double* value, bool* is_null); + void _bind_integral_result(size_t index, int64_t* value, bool* is_null); + void _bind_text_result(size_t index, const char** value, size_t* len); + void _bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null); + void _bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null); + + int size() const; + }; + + inline bind_result_t::bind_result_t(const std::shared_ptr& handle) : _handle(handle) + { + if (this->_handle && this->_handle->debug()) + { + // cerr + std::cerr << "PostgreSQL debug: constructing bind result, using handle at: " << this->_handle.get() + << std::endl; + } + } + + inline bool bind_result_t::next_impl() + { + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: accessing next row of handle at " << _handle.get() << std::endl; + } + + // Fetch total amount + if (_handle->totalCount == 0U) + { + _handle->totalCount = _handle->result.records_size(); + if (_handle->totalCount == 0U) + return false; + } + else + { + // Next row + if (_handle->count < (_handle->totalCount - 1)) + { + _handle->count++; + } + else + { + return false; + } + } + + // Really needed? + if (_handle->fields == 0U) + { + _handle->fields = _handle->result.field_count(); + } + + return true; + } + + inline void bind_result_t::_bind_boolean_result(size_t _index, signed char* value, bool* is_null) + { + auto index = static_cast(_index); + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: binding boolean result at index: " << index << std::endl; + } + + *is_null = _handle->result.isNull(_handle->count, index); + *value = _handle->result.getValue(_handle->count, index); + } + + inline void bind_result_t::_bind_floating_point_result(size_t _index, double* value, bool* is_null) + { + auto index = static_cast(_index); + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: binding floating_point result at index: " << index << std::endl; + } + + *is_null = _handle->result.isNull(_handle->count, index); + *value = _handle->result.getValue(_handle->count, index); + } + + inline void bind_result_t::_bind_integral_result(size_t _index, int64_t* value, bool* is_null) + { + auto index = static_cast(_index); + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: binding integral result at index: " << index << std::endl; + } + + *is_null = _handle->result.isNull(_handle->count, index); + *value = _handle->result.getValue(_handle->count, index); + } + + inline void bind_result_t::_bind_text_result(size_t _index, const char** value, size_t* len) + { + auto index = static_cast(_index); + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: binding text result at index: " << index << std::endl; + } + + if (_handle->result.isNull(_handle->count, index)) + { + *value = nullptr; + *len = 0; + } + else + { + *value = _handle->result.getValue(_handle->count, index); + *len = _handle->result.length(_handle->count, index); + } + } + + // same parsing logic as SQLite connector + // PostgreSQL will return one of those (using the default ISO client): + // + // 2010-10-11 01:02:03 - ISO timestamp without timezone + // 2011-11-12 01:02:03.123456 - ISO timesapt with sub-second (microsecond) precision + // 1997-12-17 07:37:16-08 - ISO timestamp with timezone + // 1992-10-10 01:02:03-06:30 - for some timezones with non-hour offset + // 1900-01-01 - date only + // we do not support time-only values ! + namespace detail + { + inline auto check_first_digit(const char* text, bool digitFlag) -> bool + { + if (digitFlag) + { + if (not std::isdigit(*text)) + { + return false; + } + } + else + { + if (std::isdigit(*text) or *text == '\0') + { + return false; + } + } + return true; + } + + inline auto check_date_digits(const char* text) -> bool + { + for (const auto digitFlag : {true, true, true, true, false, true, true, false, true, true}) // YYYY-MM-DD + { + if (not check_first_digit(text, digitFlag)) + return false; + ++text; + } + return true; + } + + inline auto check_time_digits(const char* text) -> bool + { + for (const auto digitFlag : {true, true, false, true, true, false, true, true}) // hh:mm:ss + { + if (not check_first_digit(text, digitFlag)) + return false; + ++text; + } + return true; + } + + inline auto check_us_digits(const char* text) -> bool + { + for (const auto digitFlag : {true, true, true, true, true, true}) + { + if (not check_first_digit(text, digitFlag)) + return false; + ++text; + } + return true; + } + + inline auto check_tz_digits(const char* text) -> bool + { + for (const auto digitFlag : {false, true, true, false, true, true}) + { + if (not check_first_digit(text, digitFlag)) + return false; + ++text; + } + return true; + } + } // namespace + + inline void bind_result_t::_bind_date_result(size_t _index, ::sqlpp::chrono::day_point* value, bool* is_null) + { + auto index = static_cast(_index); + + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: binding date result at index: " << index << std::endl; + } + + *is_null = _handle->result.isNull(_handle->count, index); + + if (!(*is_null)) + { + const auto date_string = _handle->result.getValue(_handle->count, index); + + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: date string: " << date_string << std::endl; + } + + if (detail::check_date_digits(date_string)) + { + const auto ymd = + ::date::year(std::atoi(date_string)) / std::atoi(date_string + 5) / std::atoi(date_string + 8); + *value = ::sqlpp::chrono::day_point(ymd); + } + else + { + if (_handle->debug()) + std::cerr << "PostgreSQL debug: got invalid date '" << date_string << "'" << std::endl; + *value = {}; + } + } + else + { + *value = {}; + } + } + + // always returns local time for timestamp with time zone + inline void bind_result_t::_bind_date_time_result(size_t _index, ::sqlpp::chrono::microsecond_point* value, bool* is_null) + { + auto index = static_cast(_index); + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: binding date_time result at index: " << index << std::endl; + } + + *is_null = _handle->result.isNull(_handle->count, index); + + if (!(*is_null)) + { + const auto date_string = _handle->result.getValue(_handle->count, index); + + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: got date_time string: " << date_string << std::endl; + } + if (detail::check_date_digits(date_string)) + { + const auto ymd = + ::date::year(std::atoi(date_string)) / std::atoi(date_string + 5) / std::atoi(date_string + 8); + *value = ::sqlpp::chrono::day_point(ymd); + } + else + { + if (_handle->debug()) + std::cerr << "PostgreSQL debug: got invalid date_time" << std::endl; + *value = {}; + return; + } + + if (std::strlen(date_string) <= 11) + return; + const auto time_string = date_string + 11; // YYYY-MM-DDT + if (detail::check_time_digits(time_string)) + { + *value += std::chrono::hours(std::atoi(time_string)) + std::chrono::minutes(std::atoi(time_string + 3)) + + std::chrono::seconds(std::atoi(time_string + 6)); + } + else + { + return; + } + + if (std::strlen(time_string) <= 9) + return; + auto us_string = time_string + 9; // hh:mm:ss. + unsigned usec = 0; + for (int i = 0; i < 6; ++i) + { + if (std::isdigit(us_string[0])) + { + usec = 10 * usec + (us_string[0] - '0'); + ++us_string; + } + else + usec *= 10; + } + *value += ::std::chrono::microseconds(usec); + } + } + + inline int bind_result_t::size() const + { + return _handle->result.records_size(); + } + } // namespace postgresql +} // namespace sqlpp + +#endif diff --git a/include/sqlpp11/postgresql/connection.h b/include/sqlpp11/postgresql/connection.h new file mode 100644 index 00000000..003c904b --- /dev/null +++ b/include/sqlpp11/postgresql/connection.h @@ -0,0 +1,692 @@ +/** + * Copyright © 2014-2015, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_CONNECTION_H +#define SQLPP_POSTGRESQL_CONNECTION_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SQLPP_DYNAMIC_LOADING +#include +#endif + +struct pg_conn; +typedef struct pg_conn PGconn; + +namespace sqlpp +{ + namespace postgresql + { +#ifdef SQLPP_DYNAMIC_LOADING + using namespace dynamic; +#endif + + namespace detail + { + // Forward declaration + inline std::unique_ptr prepare_statement(detail::connection_handle& handle, + const std::string& stmt, + const size_t& paramCount) + { + if (handle.config->debug) + { + std::cerr << "PostgreSQL debug: preparing: " << stmt << std::endl; + } + + return std::unique_ptr(new detail::prepared_statement_handle_t + (handle, stmt, paramCount)); + } + + inline void execute_prepared_statement(detail::connection_handle& handle, detail::prepared_statement_handle_t& prepared) + { + if (handle.config->debug) + { + std::cerr << "PostgreSQL debug: executing: " << prepared.name() << std::endl; + } + prepared.execute(); + } + } + + // Forward declaration + class connection; + + // Context + struct context_t + { + context_t(const connection& db) : _db(db) + { + } + context_t(const connection&&) = delete; + + template + std::ostream& operator<<(T t) + { + return _os << t; + } + + std::ostream& operator<<(bool t) + { + return _os << (t ? "TRUE" : "FALSE"); + } + + std::string escape(const std::string& arg); + + std::string str() const + { + return _os.str(); + } + + size_t count() const + { + return _count; + } + + void pop_count() + { + ++_count; + } + + const connection& _db; + std::ostringstream _os; + size_t _count{1}; + }; + + // Connection + class connection : public sqlpp::connection + { + private: + std::unique_ptr _handle; + bool _transaction_active{false}; + + void validate_connection_handle() const + { + if (!_handle) + throw std::logic_error("connection handle used, but not initialized"); + } + + // direct execution + bind_result_t select_impl(const std::string& stmt); + size_t insert_impl(const std::string& stmt); + size_t update_impl(const std::string& stmt); + size_t remove_impl(const std::string& stmt); + + // prepared execution + prepared_statement_t prepare_impl(const std::string& stmt, const size_t& paramCount); + bind_result_t run_prepared_select_impl(prepared_statement_t& prep); + size_t run_prepared_execute_impl(prepared_statement_t& prep); + size_t run_prepared_insert_impl(prepared_statement_t& prep); + size_t run_prepared_update_impl(prepared_statement_t& prep); + size_t run_prepared_remove_impl(prepared_statement_t& prep); + + public: + using _prepared_statement_t = prepared_statement_t; + using _context_t = context_t; + using _serializer_context_t = _context_t; + using _interpreter_context_t = _context_t; + + struct _tags + { + using _null_result_is_trivial_value = std::true_type; + }; + + template + static _context_t& _serialize_interpretable(const T& t, _context_t& context) + { + return ::sqlpp::serialize(t, context); + } + + template + static _context_t& _interpret_interpretable(const T& t, _context_t& context) + { + return ::sqlpp::serialize(t, context); + } + + // ctor / dtor + connection(); + connection(const std::shared_ptr& config); + ~connection(); + connection(const connection&) = delete; + connection(connection&&); + connection& operator=(const connection&) = delete; + connection& operator=(connection&&); + + // creates a connection handle and connects to database + void connectUsing(const std::shared_ptr& config) noexcept(false); + + // Select stmt (returns a result) + template + bind_result_t select(const Select& s) + { + _context_t ctx(*this); + serialize(s, ctx); + return select_impl(ctx.str()); + } + + // Prepared select + template + _prepared_statement_t prepare_select(Select& s) + { + _context_t ctx(*this); + serialize(s, ctx); + return prepare_impl(ctx.str(), ctx.count() - 1); + } + + template + bind_result_t run_prepared_select(const PreparedSelect& s) + { + s._bind_params(); + return run_prepared_select_impl(s._prepared_statement); + } + + // Insert + template + size_t insert(const Insert& i) + { + _context_t ctx(*this); + serialize(i, ctx); + return insert_impl(ctx.str()); + } + + template + prepared_statement_t prepare_insert(Insert& i) + { + _context_t ctx(*this); + serialize(i, ctx); + return prepare_impl(ctx.str(), ctx.count() - 1); + } + + template + size_t run_prepared_insert(const PreparedInsert& i) + { + i._bind_params(); + return run_prepared_insert_impl(i._prepared_statement); + } + + // Update + template + size_t update(const Update& u) + { + _context_t ctx(*this); + serialize(u, ctx); + return update_impl(ctx.str()); + } + + template + prepared_statement_t prepare_update(Update& u) + { + _context_t ctx(*this); + serialize(u, ctx); + return prepare_impl(ctx.str(), ctx.count() - 1); + } + + template + size_t run_prepared_update(const PreparedUpdate& u) + { + u._bind_params(); + return run_prepared_update_impl(u._prepared_statement); + } + + // Remove + template + size_t remove(const Remove& r) + { + _context_t ctx(*this); + serialize(r, ctx); + return remove_impl(ctx.str()); + } + + template + prepared_statement_t prepare_remove(Remove& r) + { + _context_t ctx(*this); + serialize(r, ctx); + return prepare_impl(ctx.str(), ctx.count() - 1); + } + + template + size_t run_prepared_remove(const PreparedRemove& r) + { + r._bind_params(); + return run_prepared_remove_impl(r._prepared_statement); + } + + // Execute + std::shared_ptr execute(const std::string& command); + + template < + typename Execute, + typename Enable = typename std::enable_if::value, void>::type> + std::shared_ptr execute(const Execute& x) + { + _context_t ctx(*this); + serialize(x, ctx); + return execute(ctx.str()); + } + + template + _prepared_statement_t prepare_execute(Execute& x) + { + _context_t ctx(*this); + serialize(x, ctx); + return prepare_impl(ctx.str(), ctx.count() - 1); + } + + template + size_t run_prepared_execute(const PreparedExecute& x) + { + x._prepared_statement._reset(); + x._bind_params(); + return run_prepared_execute_impl(x._prepared_statement); + } + + // escape argument + std::string escape(const std::string& s) const; + + //! call run on the argument + template + auto _run(const T& t, sqlpp::consistent_t) -> decltype(t._run(*this)) + { + return t._run(*this); + } + + template + auto _run(const T& t, Check) -> Check; + + template + auto operator()(const T& t) -> decltype(this->_run(t, sqlpp::run_check_t<_serializer_context_t, T>{})) + { + return _run(t, sqlpp::run_check_t<_serializer_context_t, T>{}); + } + + //! call prepare on the argument + template + auto _prepare(const T& t, ::sqlpp::consistent_t) -> decltype(t._prepare(*this)) + { + return t._prepare(*this); + } + + template + auto _prepare(const T& t, Check) -> Check; + + template + auto prepare(const T& t) -> decltype(this->_prepare(t, sqlpp::prepare_check_t<_serializer_context_t, T>{})) + { + return _prepare(t, sqlpp::prepare_check_t<_serializer_context_t, T>{}); + } + + //! set the default transaction isolation level to use for new transactions + void set_default_isolation_level(isolation_level level); + + //! get the currently set default transaction isolation level + isolation_level get_default_isolation_level(); + + //! create savepoint + void savepoint(const std::string& name); + + //! ROLLBACK TO SAVEPOINT + void rollback_to_savepoint(const std::string& name); + + //! release_savepoint + void release_savepoint(const std::string& name); + + //! start transaction + void start_transaction(isolation_level level = isolation_level::undefined); + + //! commit transaction (or throw transaction if transaction has + // finished already) + void commit_transaction(); + + //! rollback transaction + void rollback_transaction(bool report); + + //! report rollback failure + void report_rollback_failure(const std::string& message) noexcept; + + //! get the last inserted id for a certain table + uint64_t last_insert_id(const std::string& table, const std::string& fieldname); + + ::PGconn* native_handle(); + }; + + inline connection::connection() : _handle() + { + } + + inline connection::connection(const std::shared_ptr& config) + : _handle(new detail::connection_handle(config)) + { + } + + inline connection::~connection() + { + } + + inline connection::connection(connection&& other) + { + this->_transaction_active = other._transaction_active; + this->_handle = std::move(other._handle); + } + + inline connection& connection::operator=(connection&& other) + { + if (this != &other) + { + // TODO: check this logic + this->_transaction_active = other._transaction_active; + this->_handle = std::move(other._handle); + } + return *this; + } + + inline void connection::connectUsing(const std::shared_ptr& config) noexcept(false) + { + this->_handle.reset(new detail::connection_handle(config)); + } + + inline std::shared_ptr connection::execute(const std::string& stmt) + { + validate_connection_handle(); + if (_handle->config->debug) + { + std::cerr << "PostgreSQL debug: executing: " << stmt << std::endl; + } + + auto result = std::make_shared(*_handle); + result->result = PQexec(_handle->native(), stmt.c_str()); + result->valid = true; + + return result; + } + // direct execution + inline bind_result_t connection::select_impl(const std::string& stmt) + { + return execute(stmt); + } + + inline size_t connection::insert_impl(const std::string& stmt) + { + return execute(stmt)->result.affected_rows(); + } + + inline size_t connection::update_impl(const std::string& stmt) + { + return execute(stmt)->result.affected_rows(); + } + + inline size_t connection::remove_impl(const std::string& stmt) + { + return execute(stmt)->result.affected_rows(); + } + + // prepared execution + inline prepared_statement_t connection::prepare_impl(const std::string& stmt, const size_t& paramCount) + { + validate_connection_handle(); + return {prepare_statement(*_handle, stmt, paramCount)}; + } + + inline bind_result_t connection::run_prepared_select_impl(prepared_statement_t& prep) + { + validate_connection_handle(); + execute_prepared_statement(*_handle, *prep._handle.get()); + return {prep._handle}; + } + + inline size_t connection::run_prepared_execute_impl(prepared_statement_t& prep) + { + validate_connection_handle(); + execute_prepared_statement(*_handle, *prep._handle.get()); + return prep._handle->result.affected_rows(); + } + + inline size_t connection::run_prepared_insert_impl(prepared_statement_t& prep) + { + validate_connection_handle(); + execute_prepared_statement(*_handle, *prep._handle.get()); + return prep._handle->result.affected_rows(); + } + + inline size_t connection::run_prepared_update_impl(prepared_statement_t& prep) + { + validate_connection_handle(); + execute_prepared_statement(*_handle, *prep._handle.get()); + return prep._handle->result.affected_rows(); + } + + inline size_t connection::run_prepared_remove_impl(prepared_statement_t& prep) + { + validate_connection_handle(); + execute_prepared_statement(*_handle, *prep._handle.get()); + return prep._handle->result.affected_rows(); + } + + inline void connection::set_default_isolation_level(isolation_level level) + { + std::string level_str = "read uncommmitted"; + switch (level) + { + /// @todo what about undefined ? + case isolation_level::read_committed: + level_str = "read committed"; + break; + case isolation_level::read_uncommitted: + level_str = "read uncommitted"; + break; + case isolation_level::repeatable_read: + level_str = "repeatable read"; + break; + case isolation_level::serializable: + level_str = "serializable"; + break; + default: + throw sqlpp::exception("Invalid isolation level"); + } + std::string cmd = "SET default_transaction_isolation to '" + level_str + "'"; + execute(cmd); + } + + inline isolation_level connection::get_default_isolation_level() + { + auto res = execute("SHOW default_transaction_isolation;"); + auto status = res->result.status(); + if ((status != PGRES_TUPLES_OK) && (status != PGRES_COMMAND_OK)) + { + throw sqlpp::exception("PostgreSQL error: could not read default_transaction_isolation"); + } + + auto in = res->result.getValue(0, 0); + if (in == "read committed") + { + return isolation_level::read_committed; + } + else if (in == "read uncommitted") + { + return isolation_level::read_uncommitted; + } + else if (in == "repeatable read") + { + return isolation_level::repeatable_read; + } + else if (in == "serializable") + { + return isolation_level::serializable; + } + return isolation_level::undefined; + } + + // TODO: Fix escaping. + inline std::string connection::escape(const std::string& s) const + { + validate_connection_handle(); + // Escape strings + std::string result; + result.resize((s.size() * 2) + 1); + + int err; + size_t length = PQescapeStringConn(_handle->postgres, &result[0], s.c_str(), s.size(), &err); + result.resize(length); + return result; + } + + //! start transaction + inline void connection::start_transaction(sqlpp::isolation_level level) + { + if (_transaction_active) + { + throw sqlpp::exception("PostgreSQL error: transaction already open"); + } + switch (level) + { + case isolation_level::serializable: + { + execute("BEGIN ISOLATION LEVEL SERIALIZABLE"); + break; + } + case isolation_level::repeatable_read: + { + execute("BEGIN ISOLATION LEVEL REPEATABLE READ"); + break; + } + case isolation_level::read_committed: + { + execute("BEGIN ISOLATION LEVEL READ COMMITTED"); + break; + } + case isolation_level::read_uncommitted: + { + execute("BEGIN ISOLATION LEVEL READ UNCOMMITTED"); + break; + } + case isolation_level::undefined: + { + execute("BEGIN"); + break; + } + } + _transaction_active = true; + } + + //! create savepoint + inline void connection::savepoint(const std::string& name) + { + /// NOTE prevent from sql injection? + execute("SAVEPOINT " + name); + } + + //! ROLLBACK TO SAVEPOINT + inline void connection::rollback_to_savepoint(const std::string& name) + { + /// NOTE prevent from sql injection? + execute("ROLLBACK TO SAVEPOINT " + name); + } + + //! release_savepoint + inline void connection::release_savepoint(const std::string& name) + { + /// NOTE prevent from sql injection? + execute("RELEASE SAVEPOINT " + name); + } + + //! commit transaction (or throw transaction if transaction has finished already) + inline void connection::commit_transaction() + { + if (!_transaction_active) + { + throw sqlpp::exception("PostgreSQL error: transaction failed or finished."); + } + + _transaction_active = false; + execute("COMMIT"); + } + + //! rollback transaction + inline void connection::rollback_transaction(bool report) + { + if (!_transaction_active) + { + throw sqlpp::exception("PostgreSQL error: transaction failed or finished."); + } + execute("ROLLBACK"); + if (report) + { + std::cerr << "PostgreSQL warning: rolling back unfinished transaction" << std::endl; + } + + _transaction_active = false; + } + + //! report rollback failure + inline void connection::report_rollback_failure(const std::string& message) noexcept + { + std::cerr << "PostgreSQL error: " << message << std::endl; + } + + inline uint64_t connection::last_insert_id(const std::string& table, const std::string& fieldname) + { + std::string sql = "SELECT currval('" + table + "_" + fieldname + "_seq')"; + PGresult* res = PQexec(_handle->postgres, sql.c_str()); + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + std::string err{PQresultErrorMessage(res)}; + PQclear(res); + throw sqlpp::postgresql::undefined_table(err, sql); + } + + // Parse the number and return. + std::string in{PQgetvalue(res, 0, 0)}; + PQclear(res); + return std::stoi(in); + } + + inline ::PGconn* connection::native_handle() + { + return _handle->postgres; + } + + inline std::string context_t::escape(const std::string& arg) + { + return _db.escape(arg); + } + } +} + +#include + +#endif diff --git a/include/sqlpp11/postgresql/connection_config.h b/include/sqlpp11/postgresql/connection_config.h new file mode 100644 index 00000000..e7dac271 --- /dev/null +++ b/include/sqlpp11/postgresql/connection_config.h @@ -0,0 +1,100 @@ +/** + * Copyright © 2014-2015, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_CONNECTION_CONFIG_H +#define SQLPP_POSTGRESQL_CONNECTION_CONFIG_H + +#include +#include + +namespace sqlpp +{ + namespace postgresql + { + class connection; + struct DLL_PUBLIC connection_config + { + // Needed for the connection pool + typedef ::sqlpp::postgresql::connection connection; + + enum class sslmode_t + { + disable, + allow, + prefer, + require, + verify_ca, + verify_full + }; + std::string host; + std::string hostaddr; + uint32_t port{5432}; + std::string dbname; + std::string user; + std::string password; + uint32_t connect_timeout{0}; + std::string client_encoding; + std::string options; + std::string application_name; + std::string fallback_application_name; + bool keepalives{true}; + uint32_t keepalives_idle{0}; + uint32_t keepalives_interval{0}; + uint32_t keepalives_count{0}; + sslmode_t sslmode{sslmode_t::prefer}; + bool sslcompression{true}; + std::string sslcert; + std::string sslkey; + std::string sslrootcert; + std::string sslcrl; + std::string requirepeer; + std::string krbsrvname; + std::string service; + // bool auto_reconnect {true}; + bool debug{false}; + + bool operator==(const connection_config& other) + { + return (other.host == host && other.hostaddr == hostaddr && other.port == port && other.dbname == dbname && + other.user == user && other.password == password && other.connect_timeout == connect_timeout && + other.client_encoding == client_encoding && other.options == options && + other.application_name == application_name && other.keepalives == keepalives && + other.keepalives_idle == keepalives_idle && other.keepalives_interval == keepalives_interval && + other.keepalives_count == keepalives_count && other.sslmode == sslmode && + other.sslcompression == sslcompression && other.sslcert == sslcert && other.sslkey == sslkey && + other.sslrootcert == sslrootcert && other.sslcrl == sslcrl && other.requirepeer == requirepeer && + other.krbsrvname == krbsrvname && other.service == service && other.debug == debug); + } + bool operator!=(const connection_config& other) + { + return !operator==(other); + } + }; + } +} + +#endif diff --git a/include/sqlpp11/postgresql/detail/connection_handle.h b/include/sqlpp11/postgresql/detail/connection_handle.h new file mode 100644 index 00000000..c8d96b45 --- /dev/null +++ b/include/sqlpp11/postgresql/detail/connection_handle.h @@ -0,0 +1,239 @@ +/** + * Copyright © 2014-2015, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_CONNECTION_HANDLE_H +#define SQLPP_POSTGRESQL_CONNECTION_HANDLE_H + +#include +#include +#include + +#include +#include +#include + +#ifdef SQLPP_DYNAMIC_LOADING +#include +#endif + +namespace sqlpp +{ + namespace postgresql + { + // Forward declaration + struct connection_config; + +#ifdef SQLPP_DYNAMIC_LOADING + using namespace dynamic; +#endif + + namespace detail + { + struct DLL_LOCAL connection_handle + { + const std::shared_ptr config; + PGconn* postgres{nullptr}; + std::set prepared_statement_names; + + connection_handle(const std::shared_ptr& config); + ~connection_handle(); + connection_handle(const connection_handle&) = delete; + connection_handle(connection_handle&&) = delete; + connection_handle& operator=(const connection_handle&) = delete; + connection_handle& operator=(connection_handle&&) = delete; + + PGconn* native() const + { + return postgres; + } + + void deallocate_prepared_statement(const std::string& name); + }; + + inline connection_handle::connection_handle(const std::shared_ptr& conf) : config(conf) + { +#ifdef SQLPP_DYNAMIC_LOADING + init_pg(""); +#endif + if (config->debug) + { + std::cerr << "PostgreSQL debug: connecting to the database server." << std::endl; + } + + // Open connection + std::string conninfo = ""; + if (!config->host.empty()) + { + conninfo.append("host=" + config->host); + } + if (!config->hostaddr.empty()) + { + conninfo.append(" hostaddr=" + config->hostaddr); + } + if (config->port != 5432) + { + conninfo.append(" port=" + std::to_string(config->port)); + } + if (!config->dbname.empty()) + { + conninfo.append(" dbname=" + config->dbname); + } + if (!config->user.empty()) + { + conninfo.append(" user=" + config->user); + } + if (!config->password.empty()) + { + conninfo.append(" password=" + config->password); + } + if (config->connect_timeout != 0) + { + conninfo.append(" connect_timeout=" + std::to_string(config->connect_timeout)); + } + if (!config->client_encoding.empty()) + { + conninfo.append(" client_encoding=" + config->client_encoding); + } + if (!config->options.empty()) + { + conninfo.append(" options=" + config->options); + } + if (!config->application_name.empty()) + { + conninfo.append(" application_name=" + config->application_name); + } + if (!config->fallback_application_name.empty()) + { + conninfo.append(" fallback_application_name=" + config->fallback_application_name); + } + if (!config->keepalives) + { + conninfo.append(" keepalives=0"); + } + if (config->keepalives_idle != 0) + { + conninfo.append(" keepalives_idle=" + std::to_string(config->keepalives_idle)); + } + if (config->keepalives_interval != 0) + { + conninfo.append(" keepalives_interval=" + std::to_string(config->keepalives_interval)); + } + if (config->keepalives_count != 0) + { + conninfo.append(" keepalives_count=" + std::to_string(config->keepalives_count)); + } + switch (config->sslmode) + { + case connection_config::sslmode_t::disable: + conninfo.append(" sslmode=disable"); + break; + case connection_config::sslmode_t::allow: + conninfo.append(" sslmode=allow"); + break; + case connection_config::sslmode_t::require: + conninfo.append(" sslmode=require"); + break; + case connection_config::sslmode_t::verify_ca: + conninfo.append(" sslmode=verify-ca"); + break; + case connection_config::sslmode_t::verify_full: + conninfo.append(" sslmode=verify-full"); + break; + case connection_config::sslmode_t::prefer: + break; + } + if (!config->sslcompression) + { + conninfo.append(" sslcompression=0"); + } + if (!config->sslcert.empty()) + { + conninfo.append(" sslcert=" + config->sslcert); + } + if (!config->sslkey.empty()) + { + conninfo.append(" sslkey=" + config->sslkey); + } + if (!config->sslrootcert.empty()) + { + conninfo.append(" sslrootcert=" + config->sslrootcert); + } + if (!config->requirepeer.empty()) + { + conninfo.append(" requirepeer=" + config->requirepeer); + } + if (!config->krbsrvname.empty()) + { + conninfo.append(" krbsrvname=" + config->krbsrvname); + } + if (!config->service.empty()) + { + conninfo.append(" service=" + config->service); + } + if (this->postgres) + return; + + this->postgres = PQconnectdb(conninfo.c_str()); + + if (!this->postgres) + throw std::bad_alloc(); + + if (PQstatus(this->postgres) != CONNECTION_OK) + { + std::string msg(PQerrorMessage(this->postgres)); + PQfinish(this->postgres); + throw broken_connection(std::move(msg)); + } + } + + inline connection_handle::~connection_handle() + { + // Debug + if (config->debug) + { + std::cerr << "PostgreSQL debug: closing database connection." << std::endl; + } + + // Close connection + if (this->postgres) + { + PQfinish(this->postgres); + } + } + + inline void connection_handle::deallocate_prepared_statement(const std::string& name) + { + std::string cmd = "DEALLOCATE \"" + name + "\""; + PGresult* result = PQexec(postgres, cmd.c_str()); + PQclear(result); + prepared_statement_names.erase(name); + } + } + } +} + +#endif diff --git a/include/sqlpp11/postgresql/detail/prepared_statement_handle.h b/include/sqlpp11/postgresql/detail/prepared_statement_handle.h new file mode 100644 index 00000000..72cf72ed --- /dev/null +++ b/include/sqlpp11/postgresql/detail/prepared_statement_handle.h @@ -0,0 +1,198 @@ +/** + * Copyright © 2014-2015, Matthijs Möhlmann + * Copyright © 2015-2016, Bartosz Wieczorek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_PREPARED_STATEMENT_HANDLE_H +#define SQLPP_POSTGRESQL_PREPARED_STATEMENT_HANDLE_H + +#include +#include +#include +#include + +#include +#include +#include + +#include "connection_handle.h" + +#ifdef SQLPP_DYNAMIC_LOADING +#include +#endif + +namespace sqlpp +{ + namespace postgresql + { +#ifdef SQLPP_DYNAMIC_LOADING + using namespace dynamic; +#endif + namespace detail + { + struct DLL_PUBLIC statement_handle_t + { + detail::connection_handle& connection; + Result result; + bool valid{false}; + uint32_t count{0}; + uint32_t totalCount = {0}; + uint32_t fields = {0}; + + // ctor + statement_handle_t(detail::connection_handle& _connection); + statement_handle_t(const statement_handle_t&) = delete; + statement_handle_t(statement_handle_t&&) = default; + statement_handle_t& operator=(const statement_handle_t&) = delete; + statement_handle_t& operator=(statement_handle_t&&) = default; + + virtual ~statement_handle_t(); + bool operator!() const; + void clearResult(); + + bool debug() const; + }; + + struct prepared_statement_handle_t : public statement_handle_t + { + private: + std::string _name{"xxxxxx"}; + + public: + // Store prepared statement arguments + std::vector nullValues; + std::vector paramValues; + + // ctor + prepared_statement_handle_t(detail::connection_handle& _connection, std::string stmt, const size_t& paramCount); + prepared_statement_handle_t(const prepared_statement_handle_t&) = delete; + prepared_statement_handle_t(prepared_statement_handle_t&&) = default; + prepared_statement_handle_t& operator=(const prepared_statement_handle_t&) = delete; + prepared_statement_handle_t& operator=(prepared_statement_handle_t&&) = default; + + virtual ~prepared_statement_handle_t(); + + void execute(); + + std::string name() const + { + return _name; + } + + private: + void generate_name(); + void prepare(std::string stmt); + }; + + inline statement_handle_t::statement_handle_t(connection_handle& _connection) : connection(_connection) + { + } + + inline statement_handle_t::~statement_handle_t() + { + clearResult(); + } + + inline bool statement_handle_t::operator!() const + { + return !valid; + } + + inline void statement_handle_t::clearResult() + { + if (result) + { + result.clear(); + } + } + + inline bool statement_handle_t::debug() const + { + return connection.config->debug; + } + + inline prepared_statement_handle_t::prepared_statement_handle_t(connection_handle& _connection, + std::string stmt, + const size_t& paramCount) + : statement_handle_t(_connection), nullValues(paramCount), paramValues(paramCount) + { + generate_name(); + prepare(std::move(stmt)); + } + + inline prepared_statement_handle_t::~prepared_statement_handle_t() + { + if (valid && !_name.empty()) + { + connection.deallocate_prepared_statement(_name); + } + } + + inline void prepared_statement_handle_t::execute() + { + int size = static_cast(paramValues.size()); + + std::vector values; + for (int i = 0; i < size; i++) + values.push_back(nullValues[i] ? nullptr : const_cast(paramValues[i].c_str())); + + // Execute prepared statement with the parameters. + clearResult(); + valid = false; + count = 0; + totalCount = 0; + result = PQexecPrepared(connection.postgres, _name.data(), size, values.data(), nullptr, nullptr, 0); + /// @todo validate result? is it really valid + valid = true; + } + + inline void prepared_statement_handle_t::generate_name() + { + // Generate a random name for the prepared statement + while (connection.prepared_statement_names.find(_name) != connection.prepared_statement_names.end()) + { + std::generate_n(_name.begin(), 6, []() { + constexpr static auto charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + constexpr size_t max = (sizeof(charset) - 1); + std::random_device rd; + return charset[rd() % max]; + }); + } + connection.prepared_statement_names.insert(_name); + } + + inline void prepared_statement_handle_t::prepare(std::string stmt) + { + // Create the prepared statement + result = PQprepare(connection.postgres, _name.c_str(), stmt.c_str(), 0, nullptr); + valid = true; + } + } + } +} + +#endif diff --git a/include/sqlpp11/postgresql/dynamic_libpq.h b/include/sqlpp11/postgresql/dynamic_libpq.h new file mode 100644 index 00000000..80aecf3d --- /dev/null +++ b/include/sqlpp11/postgresql/dynamic_libpq.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2015, Volker Assmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DYNAMIC_LIBPQ_H +#define DYNAMIC_LIBPQ_H + +#include + +#include + +#ifdef SQLPP_DYNAMIC_LOADING + +// Copied from PostgreSQL Internal include (internal/libpq-int.h) to enable linking +#define CMDSTATUS_LEN 64 + +struct PGresAttValue; +struct PGresParamDesc; +struct PGEvent; +struct PGMessageField; +struct PGresult_data; + +typedef struct +{ + PQnoticeReceiver noticeRec; /* notice message receiver */ + void *noticeRecArg; + PQnoticeProcessor noticeProc; /* notice message processor */ + void *noticeProcArg; +} PGNoticeHooks; + + +struct pg_result +{ + int ntups; + int numAttributes; + PGresAttDesc *attDescs; + PGresAttValue **tuples; /* each PGresTuple is an array of + * PGresAttValue's */ + int tupArrSize; /* allocated size of tuples array */ + int numParameters; + PGresParamDesc *paramDescs; + ExecStatusType resultStatus; + char cmdStatus[CMDSTATUS_LEN]; /* cmd status from the query */ + int binary; /* binary tuple values if binary == 1, + * otherwise text */ + + /* + * These fields are copied from the originating PGconn, so that operations + * on the PGresult don't have to reference the PGconn. + */ + PGNoticeHooks noticeHooks; + PGEvent *events; + int nEvents; + int client_encoding; /* encoding id */ + + /* + * Error information (all NULL if not an error result). errMsg is the + * "overall" error message returned by PQresultErrorMessage. If we have + * per-field info then it is stored in a linked list. + */ + char *errMsg; /* error message, or NULL if no error */ + PGMessageField *errFields; /* message broken into fields */ + + /* All NULL attributes in the query result point to this null string */ + char null_field[1]; + + /* + * Space management information. Note that attDescs and error stuff, if + * not null, point into allocated blocks. But tuples points to a + * separately malloc'd block, so that we can realloc it. + */ + PGresult_data *curBlock; /* most recently allocated block */ + int curOffset; /* start offset of free space in block */ + int spaceLeft; /* number of free bytes remaining in block */ +}; + +#endif + +// namespace for internal PQ function wrappers - when using this instead of libpq direct linking +// do this: +// using namespace sqlpp::postgresql::dyn_pg; +// to override the libpq functions with these function pointers + +namespace sqlpp +{ +namespace postgresql +{ +namespace dynamic +{ + +#define DYNDEFINE(NAME) extern decltype( ::NAME ) * NAME + +DYNDEFINE(PQescapeStringConn); +DYNDEFINE(PQescapeString); +DYNDEFINE(PQescapeByteaConn); +DYNDEFINE(PQescapeBytea); +DYNDEFINE(PQfreemem); +DYNDEFINE(PQexec); +DYNDEFINE(PQprepare); +DYNDEFINE(PQexecPrepared); +DYNDEFINE(PQexecParams); +DYNDEFINE(PQresultStatus); +DYNDEFINE(PQresStatus); +DYNDEFINE(PQresultErrorMessage); +DYNDEFINE(PQresultErrorField); +DYNDEFINE(PQcmdTuples); +DYNDEFINE(PQcmdStatus); +DYNDEFINE(PQgetvalue); +DYNDEFINE(PQgetlength); +DYNDEFINE(PQgetisnull); +DYNDEFINE(PQoidValue); +DYNDEFINE(PQoidStatus); +DYNDEFINE(PQfformat); +DYNDEFINE(PQntuples); +DYNDEFINE(PQnfields); +DYNDEFINE(PQnparams); +DYNDEFINE(PQclear); +DYNDEFINE(PQfinish); +DYNDEFINE(PQstatus); +DYNDEFINE(PQconnectdb); +DYNDEFINE(PQerrorMessage); + +#undef DYNDEFINE + +/// load the PostgreSQL libraries, optionally providing the filename (leave empty for default) +void init_pg(std::string libname); + +} +} +} +#endif // DYNAMIC_LIBPQ_H + diff --git a/include/sqlpp11/postgresql/exception.h b/include/sqlpp11/postgresql/exception.h new file mode 100644 index 00000000..6179b945 --- /dev/null +++ b/include/sqlpp11/postgresql/exception.h @@ -0,0 +1,417 @@ +/** + * Copyright © 2014-2015, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_EXCEPTION_H +#define SQLPP_POSTGRESQL_EXCEPTION_H + +/** + classes copied from http://pqxx.org/devprojects/libpqxx/doc/3.1/html/Reference/a00032.html +*/ + +#include "visibility.h" +#include +#include + +namespace sqlpp +{ + namespace postgresql + { + /** + * @addtogroup exception Exception classes + * + * These exception classes follow, roughly, the two-level hierarchy defined by + * the PostgreSQL error codes (see Appendix A of the PostgreSQL documentation + * corresponding to your server version). The hierarchy given here is, as yet, + * not a complete mirror of the error codes. There are some other differences + * as well, e.g. the error code statement_completion_unknown has a separate + * status in libpqxx as in_doubt_error, and too_many_connections is classified + * as a broken_connection rather than a subtype of insufficient_resources. + * + * @see http://www.postgresql.org/docs/8.1/interactive/errcodes-appendix.html + * + * @{ + */ + + /// Run-time failure encountered by sqlpp::postgresql connector, similar to std::runtime_error + class DLL_PUBLIC failure : public ::sqlpp::exception + { + virtual const std::exception& base() const noexcept + { + return *this; + } + + public: + explicit failure(std::string whatarg) : sqlpp::exception(std::move(whatarg)) + { + } + }; + + /// Exception class for lost or failed backend connection. + /** + * @warning When this happens on Unix-like systems, you may also get a SIGPIPE + * signal. That signal aborts the program by default, so if you wish to be able + * to continue after a connection breaks, be sure to disarm this signal. + * + * If you're working on a Unix-like system, see the manual page for + * @c signal (2) on how to deal with SIGPIPE. The easiest way to make this + * signal harmless is to make your program ignore it: + * + * @code + * #include + * + * int main() + * { + * signal(SIGPIPE, SIG_IGN); + * // ... + * @endcode + */ + class DLL_PUBLIC broken_connection : public failure + { + public: + broken_connection() : failure("Connection to database failed") + { + } + + explicit broken_connection(std::string whatarg) : failure(std::move(whatarg)) + { + } + }; + + /// Exception class for failed queries. + /** Carries a copy of the failed query in addition to a regular error message */ + class DLL_PUBLIC sql_error : public failure + { + std::string m_Q; + + public: + sql_error() : failure("Failed query"), m_Q() + { + } + explicit sql_error(std::string whatarg) : failure(std::move(whatarg)), m_Q() + { + } + sql_error(std::string whatarg, std::string Q) : failure(std::move(whatarg)), m_Q(std::move(Q)) + { + } + + /// The query whose execution triggered the exception + const std::string& query() const noexcept + { + return m_Q; + } + }; + + // TODO: should this be called statement_completion_unknown!? + /// "Help, I don't know whether transaction was committed successfully!" + /** Exception that might be thrown in rare cases where the connection to the + * database is lost while finishing a database transaction, and there's no way + * of telling whether it was actually executed by the backend. In this case + * the database is left in an indeterminate (but consistent) state, and only + * manual inspection will tell which is the case. + */ + class DLL_PUBLIC in_doubt_error : public failure + { + public: + explicit in_doubt_error(std::string whatarg) : failure(std::move(whatarg)) + { + } + }; + + /// Database feature not supported in current setup + class DLL_PUBLIC feature_not_supported : public sql_error + { + public: + explicit feature_not_supported(std::string err) : sql_error(std::move(err)) + { + } + feature_not_supported(std::string err, std::string Q) : sql_error(std::move(err), std::move(Q)) + { + } + }; + + /// Error in data provided to SQL statement + class DLL_PUBLIC data_exception : public sql_error + { + public: + explicit data_exception(std::string err) : sql_error(std::move(err)) + { + } + data_exception(std::string err, std::string Q) : sql_error(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC integrity_constraint_violation : public sql_error + { + public: + explicit integrity_constraint_violation(std::string err) : sql_error(std::move(err)) + { + } + integrity_constraint_violation(std::string err, std::string Q) : sql_error(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC restrict_violation : public integrity_constraint_violation + { + public: + explicit restrict_violation(std::string err) : integrity_constraint_violation(std::move(err)) + { + } + restrict_violation(std::string err, std::string Q) : integrity_constraint_violation(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC not_null_violation : public integrity_constraint_violation + { + public: + explicit not_null_violation(std::string err) : integrity_constraint_violation(std::move(err)) + { + } + not_null_violation(std::string err, std::string Q) : integrity_constraint_violation(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC foreign_key_violation : public integrity_constraint_violation + { + public: + explicit foreign_key_violation(std::string err) : integrity_constraint_violation(std::move(err)) + { + } + foreign_key_violation(std::string err, std::string Q) + : integrity_constraint_violation(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC unique_violation : public integrity_constraint_violation + { + public: + explicit unique_violation(std::string err) : integrity_constraint_violation(std::move(err)) + { + } + unique_violation(std::string err, std::string Q) : integrity_constraint_violation(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC check_violation : public integrity_constraint_violation + { + public: + explicit check_violation(std::string err) : integrity_constraint_violation(std::move(err)) + { + } + check_violation(std::string err, std::string Q) : integrity_constraint_violation(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC invalid_cursor_state : public sql_error + { + public: + explicit invalid_cursor_state(std::string err) : sql_error(std::move(err)) + { + } + invalid_cursor_state(std::string err, std::string Q) : sql_error(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC invalid_sql_statement_name : public sql_error + { + public: + explicit invalid_sql_statement_name(std::string err) : sql_error(std::move(err)) + { + } + invalid_sql_statement_name(std::string err, std::string Q) : sql_error(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC invalid_cursor_name : public sql_error + { + public: + explicit invalid_cursor_name(std::string err) : sql_error(std::move(err)) + { + } + invalid_cursor_name(std::string err, std::string Q) : sql_error(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC syntax_error : public sql_error + { + public: + /// Approximate position in string where error occurred, or -1 if unknown. + const int error_position; + + explicit syntax_error(std::string err, int pos = -1) : sql_error(std::move(err)), error_position(pos) + { + } + syntax_error(std::string err, std::string Q, int pos = -1) : sql_error(std::move(err), std::move(Q)), error_position(pos) + { + } + }; + + class DLL_PUBLIC undefined_column : public syntax_error + { + public: + explicit undefined_column(std::string err) : syntax_error(std::move(err)) + { + } + undefined_column(std::string err, std::string Q) : syntax_error(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC undefined_function : public syntax_error + { + public: + explicit undefined_function(std::string err) : syntax_error(std::move(err)) + { + } + undefined_function(std::string err, std::string Q) : syntax_error(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC undefined_table : public syntax_error + { + public: + explicit undefined_table(std::string err) : syntax_error(std::move(err)) + { + } + undefined_table(std::string err, std::string Q) : syntax_error(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC insufficient_privilege : public sql_error + { + public: + explicit insufficient_privilege(std::string err) : sql_error(std::move(err)) + { + } + insufficient_privilege(std::string err, std::string Q) : sql_error(std::move(err), std::move(Q)) + { + } + }; + + /// Resource shortage on the server + class DLL_PUBLIC insufficient_resources : public sql_error + { + public: + explicit insufficient_resources(std::string err) : sql_error(std::move(err)) + { + } + insufficient_resources(std::string err, std::string Q) : sql_error(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC disk_full : public insufficient_resources + { + public: + explicit disk_full(std::string err) : insufficient_resources(std::move(err)) + { + } + disk_full(std::string err, std::string Q) : insufficient_resources(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC out_of_memory : public insufficient_resources + { + public: + explicit out_of_memory(std::string err) : insufficient_resources(std::move(err)) + { + } + out_of_memory(std::string err, std::string Q) : insufficient_resources(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC too_many_connections : public broken_connection + { + public: + explicit too_many_connections(std::string err) : broken_connection(std::move(err)) + { + } + }; + + /// PL/pgSQL error + /** Exceptions derived from this class are errors from PL/pgSQL procedures.*/ + class DLL_PUBLIC plpgsql_error : public sql_error + { + public: + explicit plpgsql_error(std::string err) : sql_error(std::move(err)) + { + } + plpgsql_error(std::string err, std::string Q) : sql_error(std::move(err), std::move(Q)) + { + } + }; + + /// Exception raised in PL/pgSQL procedure + class DLL_PUBLIC plpgsql_raise : public plpgsql_error + { + public: + explicit plpgsql_raise(std::string err) : plpgsql_error(std::move(err)) + { + } + plpgsql_raise(std::string err, std::string Q) : plpgsql_error(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC plpgsql_no_data_found : public plpgsql_error + { + public: + explicit plpgsql_no_data_found(const std::string& err) : plpgsql_error(std::move(err)) + { + } + plpgsql_no_data_found(const std::string& err, const std::string& Q) : plpgsql_error(std::move(err), std::move(Q)) + { + } + }; + + class DLL_PUBLIC plpgsql_too_many_rows : public plpgsql_error + { + public: + explicit plpgsql_too_many_rows(std::string err) : plpgsql_error(std::move(err)) + { + } + plpgsql_too_many_rows(std::string err, std::string Q) : plpgsql_error(std::move(err), std::move(Q)) + { + } + }; + } +} + +#endif diff --git a/include/sqlpp11/postgresql/insert.h b/include/sqlpp11/postgresql/insert.h new file mode 100644 index 00000000..e9eafe54 --- /dev/null +++ b/include/sqlpp11/postgresql/insert.h @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2013-2016, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_INSERT_H +#define SQLPP_POSTGRESQL_INSERT_H + +#include +#include +#include + +namespace sqlpp +{ + namespace postgresql + { + template + using blank_insert_t = + statement_t; + + inline auto insert() -> blank_insert_t + { + return {blank_insert_t()}; + } + + template + constexpr auto insert_into(Table table) -> decltype(blank_insert_t().into(table)) + { + return {blank_insert_t().into(table)}; + } + + template + constexpr auto dynamic_insert(const Database&) -> decltype(blank_insert_t()) + { + static_assert(std::is_base_of::value, "Invalid database parameter"); + return {blank_insert_t()}; + } + + template + constexpr auto dynamic_insert_into(const Database&, Table table) -> decltype(blank_insert_t().into(table)) + { + static_assert(std::is_base_of::value, "Invalid database parameter"); + return {blank_insert_t().into(table)}; + } + } // namespace postgresql +} // namespace sqlpp + +#endif diff --git a/include/sqlpp11/postgresql/on_conflict.h b/include/sqlpp11/postgresql/on_conflict.h new file mode 100644 index 00000000..b467e23c --- /dev/null +++ b/include/sqlpp11/postgresql/on_conflict.h @@ -0,0 +1,264 @@ +/** + * Copyright © 2014-2019, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_NO_CONFLICT_H +#define SQLPP_POSTGRESQL_NO_CONFLICT_H + +#include +#include +#include + +namespace sqlpp +{ + SQLPP_VALUE_TRAIT_GENERATOR(is_on_conflict); + + SQLPP_PORTABLE_STATIC_ASSERT(assert_on_conflict_action_t, + "either do_nothing() or do_update(...) is required with on_conflict"); + + namespace postgresql + { + template + struct on_conflict_data_t + { + on_conflict_data_t(ConflictTarget column) : _column(column) + { + } + + on_conflict_data_t(const on_conflict_data_t&) = default; + on_conflict_data_t(on_conflict_data_t&&) = default; + on_conflict_data_t& operator=(const on_conflict_data_t&) = default; + on_conflict_data_t& operator=(on_conflict_data_t&&) = default; + ~on_conflict_data_t() = default; + + ConflictTarget _column; + }; + + SQLPP_PORTABLE_STATIC_ASSERT(assert_on_conflict_do_update_set_assignments_t, + "at least one argument is not an assignment in do_update()"); + SQLPP_PORTABLE_STATIC_ASSERT(assert_on_conflict_do_update_set_no_duplicates_t, + "at least one duplicate column detected in do_update()"); + SQLPP_PORTABLE_STATIC_ASSERT(assert_on_conflict_do_update_set_allowed_t, + "at least one assignment is prohibited by its column definition in do_update()"); + SQLPP_PORTABLE_STATIC_ASSERT(assert_on_conflict_do_update_set_single_table_t, + "do_update() contains assignments for columns from more than one table"); + SQLPP_PORTABLE_STATIC_ASSERT(assert_on_conflict_do_update_set_count_args_t, + "at least one assignment expression required in do_update()"); + + namespace detail + { + template + struct lhs_must_not_update + { + static constexpr auto value = sqlpp::detail::must_not_update_impl::type>::type::value; + }; + } // namespace detail + + template + using check_on_conflict_do_update_set_t = static_combined_check_t< + static_check_t::type::value...>::value, + assert_on_conflict_do_update_set_assignments_t>, + static_check_t::type...>::value, + assert_on_conflict_do_update_set_no_duplicates_t>, + static_check_t::value...>::value, + assert_on_conflict_do_update_set_allowed_t>, + static_check_t::type>...>::size::value == 1, + assert_on_conflict_do_update_set_single_table_t>>; + + template + struct check_on_conflict_do_update_static_set + { + using type = static_combined_check_t< + check_on_conflict_do_update_set_t, + static_check_t>; + }; + + template + using check_on_conflict_do_update_static_set_t = + typename check_on_conflict_do_update_static_set::type; + + template + struct on_conflict_t + { + using _traits = make_traits; + using _nodes = sqlpp::detail::type_vector; + + using _data_t = on_conflict_data_t; + + template + struct _impl_t + { + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2091069 + _impl_t() = default; + _impl_t(const _data_t& data) : _data(data) + { + } + + _data_t _data; + }; + + template + struct _base_t + { + using _data_t = on_conflict_data_t; + + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2091069 + template + _base_t(Args&&... args) : on_conflict{std::forward(args)...} + { + } + + _impl_t on_conflict; + _impl_t& operator()() + { + return on_conflict; + } + const _impl_t& operator()() const + { + return on_conflict; + } + + template + static auto _get_member(T t) -> decltype(t.on_conflict) + { + return t.on_conflict; + } + + template + using _new_statement_t = new_statement_t; + + using _consistency_check = assert_on_conflict_action_t; + + // DO NOTHING + auto do_nothing() const -> _new_statement_t> + { + return {static_cast&>(*this), + on_conflict_do_nothing_data_t{on_conflict._data}}; + } + + // DO UPDATE + template + auto do_update(Assignments... assignments) const + -> _new_statement_t, + on_conflict_do_update_t> + { + static_assert(is_column_t::value, + "conflict_target specification is required with do_update(...)"); + return {static_cast&>(*this), + on_conflict_do_update_data_t(on_conflict._data, + std::make_tuple(assignments...))}; + } + }; + }; + + struct no_on_conflict_t + { + using _traits = make_traits; + using _nodes = ::sqlpp::detail::type_vector<>; + + using _data_t = no_data_t; + + template + struct _impl_t + { + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 + _impl_t() = default; + _impl_t(const _data_t& data) : _data(data) + { + } + + _data_t _data; + }; + + template + struct _base_t + { + using _data_t = no_data_t; + + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 + template + _base_t(Args&&... args) : no_on_conflict{std::forward(args)...} + { + } + + _impl_t no_on_conflict; + _impl_t& operator()() + { + return no_on_conflict; + } + const _impl_t& operator()() const + { + return no_on_conflict; + } + + template + static auto _get_member(T t) -> decltype(t.no_on_conflict) + { + return t.no_on_conflict; + } + + using _database_t = typename Policies::_database_t; + + template + using _new_statement_t = new_statement_t; + + using _consistency_check = consistent_t; + + auto on_conflict() const -> _new_statement_t> + { + return {static_cast&>(*this), on_conflict_data_t{no_data_t{}}}; + } + + template + auto on_conflict(ConflictTarget column) const -> _new_statement_t> + { + static_assert(is_column_t::value, + "only a column is supported as conflict_target specification"); + return {static_cast&>(*this), on_conflict_data_t{column}}; + } + }; + }; + + template + postgresql::context_t& serialize(const postgresql::on_conflict_data_t&, + postgresql::context_t& context) + { + context << " ON CONFLICT ("; + context << name_of::template char_ptr(); + context << ") "; + return context; + } + + inline postgresql::context_t& serialize(const postgresql::on_conflict_data_t&, postgresql::context_t& context) + { + context << " ON CONFLICT "; + return context; + } + } // namespace postgresql +} // namespace sqlpp + +#endif diff --git a/include/sqlpp11/postgresql/on_conflict_do_nothing.h b/include/sqlpp11/postgresql/on_conflict_do_nothing.h new file mode 100644 index 00000000..e876d1f0 --- /dev/null +++ b/include/sqlpp11/postgresql/on_conflict_do_nothing.h @@ -0,0 +1,127 @@ +/** + * Copyright © 2014-2019, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_ON_CONFLICT_DO_NOTHING_H +#define SQLPP_POSTGRESQL_ON_CONFLICT_DO_NOTHING_H + +#include +#include +#include + +namespace sqlpp +{ + SQLPP_VALUE_TRAIT_GENERATOR(is_on_conflict_do_nothing) + + namespace postgresql + { + // Forward declaration + template + struct on_conflict_data_t; + + template + struct on_conflict_do_nothing_data_t + { + on_conflict_do_nothing_data_t(on_conflict_data_t column) : _column(column) + { + } + + on_conflict_do_nothing_data_t(const on_conflict_do_nothing_data_t&) = default; + on_conflict_do_nothing_data_t(on_conflict_do_nothing_data_t&&) = default; + on_conflict_do_nothing_data_t& operator=(const on_conflict_do_nothing_data_t&) = default; + on_conflict_do_nothing_data_t& operator=(on_conflict_do_nothing_data_t&&) = default; + ~on_conflict_do_nothing_data_t() = default; + + on_conflict_data_t _column; + }; + + template + struct on_conflict_do_nothing_t + { + using _traits = make_traits; + using _nodes = sqlpp::detail::type_vector; + + // Data + using _data_t = on_conflict_do_nothing_data_t; + + // Member implementation and methods + template + struct _impl_t + { + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 + _impl_t() = default; + _impl_t(const _data_t& data) : _data(data) + { + } + + _data_t _data; + }; + + // Base template to be inherited by the statement + template + struct _base_t + { + using _data_t = on_conflict_do_nothing_data_t; + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 + + template + _base_t(Args&&... args) : column{std::forward(args)...} + { + } + + _impl_t column; + _impl_t& operator()() + { + return column; + } + const _impl_t& operator()() const + { + return column; + } + + template + static auto _get_member(T t) -> decltype(t.column) + { + return t.column; + } + + // No consistency check needed, do nothing is just do nothing. + using _consistency_check = consistent_t; + }; + }; + + template + postgresql::context_t& serialize(const postgresql::on_conflict_do_nothing_data_t& o, + postgresql::context_t& context) + { + serialize(o._column, context); + context << "DO NOTHING"; + return context; + } + } // namespace postgresql +} // namespace sqlpp + +#endif diff --git a/include/sqlpp11/postgresql/on_conflict_do_update.h b/include/sqlpp11/postgresql/on_conflict_do_update.h new file mode 100644 index 00000000..50b5defe --- /dev/null +++ b/include/sqlpp11/postgresql/on_conflict_do_update.h @@ -0,0 +1,232 @@ +/** + * Copyright © 2014-2019, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_ON_CONFLICT_DO_UPDATE_H +#define SQLPP_POSTGRESQL_ON_CONFLICT_DO_UPDATE_H + +#include +#include +#include +#include +#include + +namespace sqlpp +{ + SQLPP_VALUE_TRAIT_GENERATOR(is_on_conflict_do_update) + + namespace postgresql + { + // Assignments data + template + struct on_conflict_do_update_data_t + { + on_conflict_do_update_data_t(on_conflict_data_t conflict_target, + std::tuple assignments) + : _conflict_target(conflict_target), _assignments(assignments) + { + } + + on_conflict_do_update_data_t(const on_conflict_do_update_data_t&) = default; + on_conflict_do_update_data_t(on_conflict_do_update_data_t&&) = default; + on_conflict_do_update_data_t& operator=(const on_conflict_do_update_data_t&) = default; + on_conflict_do_update_data_t& operator=(on_conflict_do_update_data_t&&) = default; + ~on_conflict_do_update_data_t() = default; + + on_conflict_data_t _conflict_target; + std::tuple _assignments; + // interpretable_list_t _dynamic_assignments; + }; + + // Where data + template + struct on_conflict_do_update_where_data_t + { + on_conflict_do_update_where_data_t( + Expression expression, on_conflict_do_update_data_t assignments) + : _expression(expression), _assignments(assignments) + { + } + + on_conflict_do_update_where_data_t(const on_conflict_do_update_where_data_t&) = default; + on_conflict_do_update_where_data_t(on_conflict_do_update_where_data_t&&) = default; + on_conflict_do_update_where_data_t& operator=(const on_conflict_do_update_where_data_t&) = default; + on_conflict_do_update_where_data_t& operator=(on_conflict_do_update_where_data_t&&) = default; + + Expression _expression; + on_conflict_do_update_data_t _assignments; + // interpretable_list_t _dynamic_expressions; + }; + + // extra where statement + template + struct on_conflict_do_update_where_t + { + using _traits = make_traits; + using _nodes = sqlpp::detail::type_vector; + using _is_dynamic = is_database; + + using _data_t = on_conflict_do_update_where_data_t; + + // Member implementation and methods + template + struct _impl_t + { + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 + _impl_t() = default; + _impl_t(const _data_t& data) : _data(data) + { + } + + _data_t _data; + }; + + template + struct _base_t + { + using _data_t = on_conflict_do_update_where_data_t; + + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 + template + _base_t(Args&&... args) : where{std::forward(args)...} + { + } + + _impl_t where; + _impl_t& operator()() + { + return where; + } + const _impl_t& operator()() const + { + return where; + } + + template + static auto _get_member(T t) -> decltype(t.where) + { + return t.where; + } + + using _consistency_check = consistent_t; + }; + }; + + // Use the update_list + template + struct on_conflict_do_update_t + { + using _traits = make_traits; + using _nodes = sqlpp::detail::type_vector; + using _is_dynamic = is_database; + + // Data + using _data_t = on_conflict_do_update_data_t; + + // Member implementation with data and methods + template + struct _impl_t + { + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 + _impl_t() = default; + _impl_t(const _data_t& data) : _data(data) + { + } + + _data_t _data; + }; + + // Base template to be inherited by the statement + template + struct _base_t + { + using _data_t = on_conflict_do_update_data_t; + + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 + template + _base_t(Args&&... args) : assignments{std::forward(args)...} + { + } + + _impl_t assignments; + _impl_t& operator()() + { + return assignments; + } + const _impl_t& operator()() const + { + return assignments; + } + + template + static auto _get_member(T t) -> decltype(t.assignments) + { + return t.assignments; + } + + using _consistency_check = consistent_t; + + template + using _new_statement_t = new_statement_t; + + // WHERE + template + auto where(Expression expression) const + -> _new_statement_t, + on_conflict_do_update_where_t> + { + return {static_cast&>(*this), + on_conflict_do_update_where_data_t( + expression, assignments._data)}; + } + }; + }; + + template + postgresql::context_t& serialize( + const postgresql::on_conflict_do_update_data_t& o, + postgresql::context_t& context) + { + serialize(o._conflict_target, context); + context << "DO UPDATE SET "; + interpret_tuple(o._assignments, ",", context); + return context; + } + + template + postgresql::context_t& serialize( + const postgresql::on_conflict_do_update_where_data_t& o, + postgresql::context_t& context) + { + serialize(o._assignments, context); + context << " WHERE "; + serialize(o._expression, context); + return context; + } + } // namespace postgresql +} // namespace sqlpp + +#endif diff --git a/include/sqlpp11/postgresql/postgresql.h b/include/sqlpp11/postgresql/postgresql.h new file mode 100644 index 00000000..84e4cfa9 --- /dev/null +++ b/include/sqlpp11/postgresql/postgresql.h @@ -0,0 +1,36 @@ +/** + * Copyright © 2014-2015, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_H +#define SQLPP_POSTGRESQL_H + +#include +#include +#include +#include + +#endif diff --git a/include/sqlpp11/postgresql/prepared_statement.h b/include/sqlpp11/postgresql/prepared_statement.h new file mode 100644 index 00000000..1681554d --- /dev/null +++ b/include/sqlpp11/postgresql/prepared_statement.h @@ -0,0 +1,216 @@ +/** + * Copyright © 2014-2015, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_PREPARED_STATEMENT_H +#define SQLPP_POSTGRESQL_PREPARED_STATEMENT_H + +#include +#include +#include + +#include + +namespace sqlpp +{ + namespace postgresql + { +#ifdef SQLPP_DYNAMIC_LOADING + using namespace dynamic; +#endif + + // Forward declaration + class connection; + + // Detail namespace + namespace detail + { + struct prepared_statement_handle_t; + } + + class prepared_statement_t + { + friend sqlpp::postgresql::connection; + + private: + std::shared_ptr _handle; + + public: + prepared_statement_t() = default; + prepared_statement_t(std::shared_ptr&& handle); + prepared_statement_t(const prepared_statement_t&) = delete; + prepared_statement_t(prepared_statement_t&&) = default; + prepared_statement_t& operator=(const prepared_statement_t&) = delete; + prepared_statement_t& operator=(prepared_statement_t&&) = default; + ~prepared_statement_t() = default; + + bool operator==(const prepared_statement_t& rhs) + { + return (this->_handle == rhs._handle); + } + + void _bind_boolean_parameter(size_t index, const signed char* value, bool is_null); + void _bind_floating_point_parameter(size_t index, const double* value, bool is_null); + void _bind_integral_parameter(size_t index, const int64_t* value, bool is_null); + void _bind_text_parameter(size_t index, const std::string* value, bool is_null); + void _bind_date_parameter(size_t index, const ::sqlpp::chrono::day_point* value, bool is_null); + void _bind_date_time_parameter(size_t index, const ::sqlpp::chrono::microsecond_point* value, bool is_null); + }; + + // ctor + inline prepared_statement_t::prepared_statement_t(std::shared_ptr&& handle) + : _handle{handle} + { + if (_handle && _handle->debug()) + { + std::cerr << "PostgreSQL debug: constructing prepared_statement, using handle at: " << _handle.get() + << std::endl; + } + } + + inline void prepared_statement_t::_bind_boolean_parameter(size_t index, const signed char* value, bool is_null) + { + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: binding boolean parameter " << (*value ? "true" : "false") + << " at index: " << index << ", being " << (is_null ? "" : "not ") << "null" << std::endl; + } + + _handle->nullValues[index] = is_null; + if (!is_null) + { + if (*value) + { + _handle->paramValues[index] = "TRUE"; + } + else + { + _handle->paramValues[index] = "FALSE"; + } + } + } + + inline void prepared_statement_t::_bind_floating_point_parameter(size_t index, const double* value, bool is_null) + { + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: binding floating_point parameter " << *value << " at index: " << index + << ", being " << (is_null ? "" : "not ") << "null" << std::endl; + } + + _handle->nullValues[index] = is_null; + if (!is_null) + { + std::ostringstream out; + out.precision(std::numeric_limits::digits10); + out << std::fixed << *value; + _handle->paramValues[index] = out.str(); + } + } + + inline void prepared_statement_t::_bind_integral_parameter(size_t index, const int64_t* value, bool is_null) + { + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: binding integral parameter " << *value << " at index: " << index << ", being " + << (is_null ? "" : "not ") << "null" << std::endl; + } + + // Assign values + _handle->nullValues[index] = is_null; + if (!is_null) + { + _handle->paramValues[index] = std::to_string(*value); + } + } + + inline void prepared_statement_t::_bind_text_parameter(size_t index, const std::string* value, bool is_null) + { + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: binding text parameter " << *value << " at index: " << index << ", being " + << (is_null ? "" : "not ") << "null" << std::endl; + } + + // Assign values + _handle->nullValues[index] = is_null; + if (!is_null) + { + _handle->paramValues[index] = *value; + } + } + + inline void prepared_statement_t::_bind_date_parameter(size_t index, const ::sqlpp::chrono::day_point* value, bool is_null) + { + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: binding date parameter at index " + << index << ", being " << (is_null ? "" : "not ") << "null" << std::endl; + } + _handle->nullValues[index] = is_null; + if (not is_null) + { + const auto ymd = ::date::year_month_day{*value}; + std::ostringstream os; + os << ymd; + _handle->paramValues[index] = os.str(); + + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: binding date parameter string: " << _handle->paramValues[index] << std::endl; + } + } + } + + inline void prepared_statement_t::_bind_date_time_parameter(size_t index, const ::sqlpp::chrono::microsecond_point* value, bool is_null) + { + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: binding date_time parameter at index " + << index << ", being " << (is_null ? "" : "not ") << "null" << std::endl; + } + _handle->nullValues[index] = is_null; + if (not is_null) + { + const auto dp = ::sqlpp::chrono::floor<::date::days>(*value); + const auto time = ::date::make_time(::sqlpp::chrono::floor<::std::chrono::microseconds>(*value - dp)); + const auto ymd = ::date::year_month_day{dp}; + + // Timezone handling - always treat the value as UTC. + // It is assumed that the database timezone is set to UTC, too. + std::ostringstream os; + os << ymd << ' ' << time; + _handle->paramValues[index] = os.str(); + if (_handle->debug()) + { + std::cerr << "PostgreSQL debug: binding date_time parameter string: " << _handle->paramValues[index] << std::endl; + } + } + } + } +} + +#endif diff --git a/include/sqlpp11/postgresql/result.h b/include/sqlpp11/postgresql/result.h new file mode 100644 index 00000000..be86bcba --- /dev/null +++ b/include/sqlpp11/postgresql/result.h @@ -0,0 +1,346 @@ +/** + * Copyright © 2015-2016, Bartosz Wieczorek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_RESULT_H +#define SQLPP_POSTGRESQL_RESULT_H + +#include +#include +#include +#include + +#include + +#include +#include + +#ifdef SQLPP_DYNAMIC_LOADING +#include +#endif + +namespace sqlpp +{ + namespace postgresql + { +#ifdef SQLPP_DYNAMIC_LOADING + using namespace dynamic; +#endif + + class DLL_PUBLIC Result + { + public: + Result(); + ~Result(); + + ExecStatusType status(); + + void clear(); + + int affected_rows(); + int records_size() const; + int field_count() const; + int length(int record, int field) const; + bool isNull(int record, int field) const; + void operator=(PGresult* res); + operator bool() const; + + template + inline T getValue(int record, int field) const + { + static_assert(std::is_arithmetic::value, "Value must be numeric type"); + checkIndex(record, field); + T t(0); + auto txt = std::string(getPqValue(m_result, record, field)); + if(txt != "") + + { + t = std::stold(txt); + } + return t; + } + + const std::string& query() const + { + return m_query; + } + std::string& query() + { + return m_query; + } + + private: + void CheckStatus() const; + [[noreturn]] void ThrowSQLError(const std::string& Err, const std::string& Query) const; + std::string StatusError() const; + int errorPosition() const noexcept; + bool hasError(); + void checkIndex(int record, int field) const noexcept(false); + + // move PQgetvalue to implementation so we don't depend on the libpq in the + // public interface + const char* getPqValue(PGresult* result, int record, int field) const; + + PGresult* m_result; + std::string m_query; + }; + + template <> + inline const char* Result::getValue(int record, int field) const + { + return const_cast(getPqValue(m_result, record, field)); + } + + template <> + inline std::string Result::getValue(int record, int field) const + { + return {getValue(record, field)}; + } + + template <> + inline bool Result::getValue(int record, int field) const + { + checkIndex(record, field); + auto val = getPqValue(m_result, record, field); + if (*val == 't') + return true; + else if (*val == 'f') + return false; + return const_cast(val); + } + + inline Result::Result() : m_result(nullptr) + { + } + + inline void Result::checkIndex(int record, int field) const noexcept(false) + { + if (record > records_size() || field > field_count()) + throw std::out_of_range("PostgreSQL error: index out of range"); + } + + inline void Result::operator=(PGresult* res) + { + m_result = res; + CheckStatus(); + } + + inline void Result::CheckStatus() const + { + const std::string Err = StatusError(); + if (!Err.empty()) + ThrowSQLError(Err, query()); + } + + inline const char* Result::getPqValue(PGresult* result, int record, int field) const + { + return const_cast(PQgetvalue(result, record, field)); + } + + [[noreturn]] inline void Result::ThrowSQLError(const std::string& Err, const std::string& Query) const + { + // Try to establish more precise error type, and throw corresponding exception + const char* const code = PQresultErrorField(m_result, PG_DIAG_SQLSTATE); + if (code) + switch (code[0]) + { + case '0': + switch (code[1]) + { + case '8': + throw broken_connection(Err); + case 'A': + throw feature_not_supported(Err, Query); + } + break; + case '2': + switch (code[1]) + { + case '2': + throw data_exception(Err, Query); + case '3': + if (strcmp(code, "23001") == 0) + throw restrict_violation(Err, Query); + if (strcmp(code, "23502") == 0) + throw not_null_violation(Err, Query); + if (strcmp(code, "23503") == 0) + throw foreign_key_violation(Err, Query); + if (strcmp(code, "23505") == 0) + throw unique_violation(Err, Query); + if (strcmp(code, "23514") == 0) + throw check_violation(Err, Query); + throw integrity_constraint_violation(Err, Query); + case '4': + throw invalid_cursor_state(Err, Query); + case '6': + throw invalid_sql_statement_name(Err, Query); + } + break; + case '3': + switch (code[1]) + { + case '4': + throw invalid_cursor_name(Err, Query); + } + break; + case '4': + switch (code[1]) + { + case '2': + if (strcmp(code, "42501") == 0) + throw insufficient_privilege(Err, Query); + if (strcmp(code, "42601") == 0) + throw syntax_error(Err, Query, errorPosition()); + if (strcmp(code, "42703") == 0) + throw undefined_column(Err, Query); + if (strcmp(code, "42883") == 0) + throw undefined_function(Err, Query); + if (strcmp(code, "42P01") == 0) + throw undefined_table(Err, Query); + } + break; + case '5': + switch (code[1]) + { + case '3': + if (strcmp(code, "53100") == 0) + throw disk_full(Err, Query); + if (strcmp(code, "53200") == 0) + throw out_of_memory(Err, Query); + if (strcmp(code, "53300") == 0) + throw too_many_connections(Err); + throw insufficient_resources(Err, Query); + } + break; + + case 'P': + if (strcmp(code, "P0001") == 0) + throw plpgsql_raise(Err, Query); + if (strcmp(code, "P0002") == 0) + throw plpgsql_no_data_found(Err, Query); + if (strcmp(code, "P0003") == 0) + throw plpgsql_too_many_rows(Err, Query); + throw plpgsql_error(Err, Query); + } + throw sql_error(Err, Query); + } + + inline std::string Result::StatusError() const + { + if (!m_result) + throw failure("No result set given"); + + std::string Err; + + switch (PQresultStatus(m_result)) + { + case PGRES_EMPTY_QUERY: // The string sent to the backend was empty. + case PGRES_COMMAND_OK: // Successful completion of a command returning no data + case PGRES_TUPLES_OK: // The query successfully executed + break; + + case PGRES_COPY_OUT: // Copy Out (from server) data transfer started + case PGRES_COPY_IN: // Copy In (to server) data transfer started + break; + + case PGRES_BAD_RESPONSE: // The server's response was not understood + case PGRES_NONFATAL_ERROR: + case PGRES_FATAL_ERROR: + Err = PQresultErrorMessage(m_result); + break; + case PGRES_COPY_BOTH: + case PGRES_SINGLE_TUPLE: + throw sqlpp::exception("pqxx::result: Unrecognized response code " + + std::to_string(PQresultStatus(m_result))); + } + return Err; + } + + inline int Result::errorPosition() const noexcept + { + int pos = -1; + if (m_result) + { + const char* p = PQresultErrorField(m_result, PG_DIAG_STATEMENT_POSITION); + if (p) + pos = std::stoi(std::string(p)); + } + return pos; + } + + inline sqlpp::postgresql::Result::operator bool() const + { + return m_result != 0; + } + + inline void Result::clear() + { + if (m_result) + PQclear(m_result); + m_result = nullptr; + } + + inline int Result::affected_rows() + { + const char* const RowsStr = PQcmdTuples(m_result); + return RowsStr[0] ? std::stoi(std::string(RowsStr)) : 0; + } + + inline int Result::records_size() const + { + return m_result ? PQntuples(m_result) : 0; + } + + inline int Result::field_count() const + { + return m_result ? PQnfields(m_result) : 0; + } + + inline bool Result::isNull(int record, int field) const + { + /// check index? + return PQgetisnull(m_result, record, field); + } + + inline int Result::length(int record, int field) const + { + /// check index? + return PQgetlength(m_result, record, field); + } + + inline Result::~Result() + { + clear(); + } + + inline ExecStatusType Result::status() + { + return PQresultStatus(m_result); + } + } +} + +#endif diff --git a/include/sqlpp11/postgresql/returning.h b/include/sqlpp11/postgresql/returning.h new file mode 100644 index 00000000..5ecab977 --- /dev/null +++ b/include/sqlpp11/postgresql/returning.h @@ -0,0 +1,176 @@ +/** + * Copyright © 2014-2018, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_RETURNING_H +#define SQLPP_POSTGRESQL_RETURNING_H + +#include +#include +#include + +namespace sqlpp +{ + SQLPP_VALUE_TRAIT_GENERATOR(is_returning) + + namespace postgresql + { + struct return_name_t + { + }; + + struct returning_t : public statement_name_t + { + }; + + template + using blank_returning_t = statement_t; + + inline blank_returning_t returning() + { + return {}; + } + + struct no_returning_t + { + using _traits = make_traits; + using _nodes = ::sqlpp::detail::type_vector<>; + + using _data_t = no_data_t; + + template + struct _impl_t + { + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 + _impl_t() = default; + _impl_t(const _data_t& data) : _data(data) + { + } + + _data_t _data; + }; + + template + struct _base_t + { + using _data_t = no_data_t; + + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 + template + _base_t(Args&&... args) : no_returning{std::forward(args)...} + { + } + + _impl_t no_returning; + _impl_t& operator()() + { + return no_returning; + } + const _impl_t& operator()() const + { + return no_returning; + } + + template + static auto _get_member(T t) -> decltype(t.no_returning) + { + return t.no_returning; + } + + using _database_t = typename Policies::_database_t; + + template + static constexpr auto _check_tuple(std::tuple) -> check_selected_columns_t + { + return {}; + } + + template + static constexpr auto _check_args(T... args) + -> decltype(_check_tuple(sqlpp::detail::column_tuple_merge(args...))) + { + return {}; + } + + template + using _new_statement_t = new_statement_t; + + using _consistency_check = consistent_t; + + template + auto returning(Columns... columns) const + -> _new_statement_t> + { + static_assert(sizeof...(Columns), + "at least one returnable expression (e.g. a column) is required in returning"); + static_assert(decltype(_check_args(columns...))::value, + "at least one argument is not a returnable expression in returning()"); + return {static_cast&>(*this), + typename returning_column_list_t<_database_t, Columns...>::_data_t{columns...}}; + } + + template + auto returning(std::tuple columns) const + -> _new_statement_t> + { + static_assert(sizeof...(Columns), + "at least one returnable expression (e.g. a column) is required in returning"); + static_assert(decltype(_check_args(columns))::value, + "at least one argument is not a returnable expression in returning()"); + return {static_cast&>(*this), + typename returning_column_list_t<_database_t, Columns...>::_data_t{columns}}; + } + + template + auto dynamic_returning(Columns... columns) const + -> _new_statement_t> + { + static_assert(sizeof...(Columns), + "at least one returnable expression (e.g. a column) is required in returning"); + static_assert(decltype(_check_args(columns...))::value, + "at least one argument is not a returnable expression in returning()"); + return {static_cast&>(*this), + typename returning_column_list_t<_database_t, Columns...>::_data_t{columns...}}; + } + + template + auto dynamic_returning(const Database&, Columns... columns) + -> decltype(blank_returning_t().columns(columns...)) + { + static_assert(sizeof...(Columns), + "at least one returnable expression (e.g. a column) is required in returning"); + static_assert(decltype(_check_args(columns...))::value, + "at least one argument is not a returnable expression in returning()"); + static_assert(std::is_base_of::value, "Invalid database parameter"); + return {static_cast&>(*this), + typename dynamic_returning_column_list<_database_t>::_data_t{columns...}}; + } + }; + }; + } // namespace postgresql +} // namespace sqlpp + +#endif diff --git a/include/sqlpp11/postgresql/returning_column_list.h b/include/sqlpp11/postgresql/returning_column_list.h new file mode 100644 index 00000000..051049ce --- /dev/null +++ b/include/sqlpp11/postgresql/returning_column_list.h @@ -0,0 +1,496 @@ +/** + * Copyright © 2014-2015, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_RETURNING_COLUMN_LIST_H +#define SQLPP_POSTGRESQL_RETURNING_COLUMN_LIST_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sqlpp +{ + SQLPP_VALUE_TRAIT_GENERATOR(is_returning_column_list) + + namespace postgresql + { + namespace detail + { + template + struct returning_traits + { + using _traits = make_traits; + struct _alias_t + { + }; + }; + + template + struct returning_traits + { + using _traits = make_traits, + tag::is_returning_column_list, + tag::is_return_value, + tag::is_expression, + tag::is_selectable>; // TODO: Is this correct? + using _alias_t = typename Column::_alias_t; + }; + } // namespace detail + + template + struct dynamic_returning_column_list + { + using _names_t = std::vector; + std::vector> _dynamic_columns; + _names_t _dynamic_expression_names; + + template + void emplace_back(Expr expr) + { + _dynamic_expression_names.push_back(name_of::char_ptr()); + _dynamic_columns.emplace_back(expr); + } + + bool empty() const + { + return _dynamic_columns.empty(); + } + }; + + template <> + struct dynamic_returning_column_list + { + using _names_t = no_name_t; + _names_t _dynamic_expression_names; + + static constexpr bool empty() + { + return true; + } + }; + + template + postgresql::context_t& serialize(const postgresql::dynamic_returning_column_list& t, + postgresql::context_t& context) + { + bool first{true}; + for (const auto& column : t._dynamic_columns) + { + if (first) + { + first = false; + } + else + { + context << ','; + } + + serialize(column, context); + } + + return context; + } + + inline postgresql::context_t& serialize(const postgresql::dynamic_returning_column_list&, + postgresql::context_t& context) + { + return context; + } + + template + struct returning_column_list_data_t + { + returning_column_list_data_t(Columns... columns) : _columns(columns...) + { + } + returning_column_list_data_t(std::tuple columns) : _columns(columns) + { + } + returning_column_list_data_t(const returning_column_list_data_t&) = default; + returning_column_list_data_t(returning_column_list_data_t&&) = default; + returning_column_list_data_t& operator=(const returning_column_list_data_t&) = default; + returning_column_list_data_t& operator=(returning_column_list_data_t&&) = default; + + std::tuple _columns; + dynamic_returning_column_list _dynamic_columns; + }; + + // static asserts... + SQLPP_PORTABLE_STATIC_ASSERT( + assert_no_unknown_tables_in_returning_columns_t, + "at least one returning column requires a table which is otherwise not known in the statement"); + + // Columns in returning list + template + struct returning_column_list_t + { + using _traits = typename detail::returning_traits::_traits; + using _nodes = ::sqlpp::detail::type_vector; + using _alias_t = typename detail::returning_traits::_alias_t; + using _is_dynamic = is_database; + + struct _column_type + { + }; + + // Data + using _data_t = returning_column_list_data_t; + + // Implementation + template + struct _impl_t + { + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 + _impl_t() = default; + _impl_t(const _data_t& data) : _data(data) + { + } + + template + void add_ntc(NamedExpression namedExpression) + { + add(namedExpression); + } + + template + void add(NamedExpression namedExpression) + { + using named_expression = auto_alias_t; + static_assert(_is_dynamic::value, "selected_columns::add() can only be called for dynamic_column"); + static_assert(is_selectable_t::value, + "invalid named expression argument in selected_columns::add()"); + static_assert(TableCheckRequired::value or Policies::template _no_unknown_tables::value, + "named expression uses tables unknown to this statement in selected_columns::add()"); + using column_names = ::sqlpp::detail::make_type_set_t; + static_assert(not::sqlpp::detail::is_element_of::value, + "a column of this name is present in the select already"); + + using ok = logic::all_t<_is_dynamic::value, is_selectable_t::value>; + + _add_impl(namedExpression, ok()); + } + + template + void _add_impl(NamedExpression namedExpression, const std::true_type&) + { + return _data._dynamic_columns.emplace_back(auto_alias_t{namedExpression}); + } + + template + void _add_impl(NamedExpression namedExpression, const std::false_type&); + + _data_t _data; + }; + + // Base template to be inherited by statement + template + struct _base_t + { + using _data_t = returning_column_list_data_t; + + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 + template + _base_t(Args&&... args) : returning_columns{std::forward(args)...} + { + } + + _impl_t returning_columns; + _impl_t& operator()() + { + return returning_columns; + } + const _impl_t& operator()() const + { + return returning_columns; + } + + _impl_t& get_selected_columns() + { + return returning_columns; + } + const _impl_t& get_selected_columns() const + { + return returning_columns; + } + + template + static auto _get_member(T t) -> decltype(t.returning_columms) + { + return t.returning_columns; + } + + // Checks + using _table_check = + typename std::conditional::value, + consistent_t, + assert_no_unknown_tables_in_returning_columns_t>::type; + + using _consistency_check = ::sqlpp::detail::get_first_if; + }; + + // Result methods + template + struct _result_methods_t + { + using _statement_t = Statement; + + const _statement_t& _get_statement() const + { + return static_cast(*this); + } + + template + struct _deferred_field_t + { + using type = make_field_spec_t<_statement_t, Column>; + }; + + template + using _field_t = typename _deferred_field_t::type; + + template + using _result_row_t = typename std::conditional<_is_dynamic::value, + dynamic_result_row_t...>, + result_row_t...>>::type; + + using _dynamic_names_t = typename dynamic_returning_column_list::_names_t; + + template + struct _deferred_table_t + { + using table = select_pseudo_table_t<_statement_t, Columns...>; + using alias = typename table::template _alias_t; + }; + + template + using _table_t = typename _deferred_table_t::table; + + template + using _alias_t = typename _deferred_table_t::alias; + + template + _alias_t as(const AliasProvider& aliasProvider) const + { + consistency_check_t<_statement_t>::_(); + static_assert(_statement_t::_can_be_used_as_table(), + "statement cannot be used as table, e.g. due to missing tables"); + return _table_t(_get_statement()).as(aliasProvider); + } + + const _dynamic_names_t& get_dynamic_names() const + { + return _get_statement().get_selected_columns()._data._dynamic_columns._dynamic_expression_names; + } + + size_t get_no_of_result_columns() const + { + return sizeof...(Columns) + get_dynamic_names().size(); + } + + // auto .. + template + auto _run(Db& db, const Composite& composite) const + -> result_t> + { + return {db.select(composite), get_dynamic_names()}; + } + + template + auto _run(Db& db) const -> result_t())), _result_row_t> + { + return {db.select(_get_statement()), get_dynamic_names()}; + } + + // Prepare + template + auto _prepare(Db& db, const Composite& composite) const -> prepared_select_t + { + return {make_parameter_list_t{}, get_dynamic_names(), db.prepare_select(composite)}; + } + + template + auto _prepare(Db& db) const -> prepared_select_t + { + return {make_parameter_list_t<_statement_t>{}, get_dynamic_names(), db.prepare_select(_get_statement())}; + } + }; + }; + + namespace detail + { + template + returning_column_list_t make_returning_column_list(std::tuple columns); + } + + struct no_returning_column_list_t + { + using _traits = make_traits; + using _nodes = ::sqlpp::detail::type_vector<>; + + struct _alias_t + { + }; + + // Data + using _data_t = no_data_t; + + // Member implementation with data and methods + template + struct _impl_t + { + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 + _impl_t() = default; + _impl_t(const _data_t& data) : _data(data) + { + } + + _data_t _data; + }; + + // Base template to be inherited + template + struct _base_t + { + using _data_t = no_data_t; + + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 + template + _base_t(Args&... args) : no_returned_columns{std::forward(args)...} + { + } + + _impl_t no_returned_columns; + _impl_t& operator()() + { + return no_returned_columns; + } + const _impl_t& operator()() const + { + return no_returned_columns; + } + + template + static auto _get_member(T t) -> decltype(t.no_returned_columns) + { + return t.no_returned_columns; + } + + using _database_t = typename Policies::_database_t; + + template + struct _check : logic::all_t::value...> + { + }; + + template + static constexpr auto _check_tuple(std::tuple) -> _check + { + return {}; + } + + template + static constexpr auto _check_args(T... args) + -> decltype(_check_tuple(sqlpp::detail::column_tuple_merge(args...))) + { + return _check_tuple(sqlpp::detail::column_tuple_merge(args...)); + } + + template + using _new_statement_t = new_statement_t; + + using _consistency_check = consistent_t; + + template + auto returning(Args... args) const -> _new_statement_t< + decltype(_check_args(args...)), + decltype(detail::make_returning_column_list(::sqlpp::detail::column_tuple_merge(args...)))> + { + static_assert(sizeof...(Args), "at least one selectable expression (e.g. a column) required in returning()"); + static_assert(decltype(_check_args(args...))::value, + "at least one argument is not a selectable expression in returning()"); + + return _returning_impl(decltype(_check_args(args...)){}, ::sqlpp::detail::column_tuple_merge(args...)); + } + + template + auto dynamic_returning(Args... args) const + -> _new_statement_t(::sqlpp::detail::column_tuple_merge(args...)))> + { + static_assert(not std::is_same<_database_t, void>::value, + "dynamic_columns must not be called in a static statement"); + static_assert(decltype(_check_args(args...))::value, + "at least one argument is not a selectable expression in returning()"); + + return _returning_impl<_database_t>(decltype(_check_args(args...)){}, + ::sqlpp::detail::column_tuple_merge(args...)); + } + + private: + template + auto _returning_impl(const std::false_type&, std::tuple args) const -> inconsistent; + + template + auto _returning_impl(consistent_t, std::tuple args) const + -> _new_statement_t> + { + static_assert(not::sqlpp::detail::has_duplicates::value, "at least one duplicate argument detected"); + static_assert(not::sqlpp::detail::has_duplicates::value, + "at least one duplicate name detected"); + + return {static_cast&>(*this), + typename returning_column_list_t::_data_t{args}}; + } + }; + }; + + // Serialization + template + postgresql::context_t& serialize(const postgresql::returning_column_list_data_t& t, + postgresql::context_t& context) + { + context << " RETURNING "; + interpret_tuple(t._columns, ',', context); + if (sizeof...(Columns) and not t._dynamic_columns.empty()) + context << ','; + serialize(t._dynamic_columns, context); + return context; + } + } +} // namespace sqlpp + +#endif diff --git a/include/sqlpp11/postgresql/serializer.h b/include/sqlpp11/postgresql/serializer.h new file mode 100644 index 00000000..ea8eb7b7 --- /dev/null +++ b/include/sqlpp11/postgresql/serializer.h @@ -0,0 +1,46 @@ +/** + * Copyright © 2014-2015, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_SERIALIZER_H +#define SQLPP_POSTGRESQL_SERIALIZER_H + +#include +#include + +namespace sqlpp +{ + // Serialize parameters + template + postgresql::context_t& serialize(const parameter_t&, postgresql::context_t& context) + { + context << "$" << context.count(); + context.pop_count(); + return context; + } +} + +#endif diff --git a/include/sqlpp11/postgresql/update.h b/include/sqlpp11/postgresql/update.h new file mode 100644 index 00000000..8d72cd80 --- /dev/null +++ b/include/sqlpp11/postgresql/update.h @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2016, Bartoszek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_POSTGRESQL_UPDATE_H +#define SQLPP_POSTGRESQL_UPDATE_H + +#include +#include + +namespace sqlpp +{ + namespace postgresql + { + template + using blank_update_t = + statement_t, no_returning_t>; + + template + constexpr auto update(Table table) -> decltype(blank_update_t().single_table(table)) + { + return {blank_update_t().single_table(table)}; + } + + template + constexpr auto dynamic_update(const Database&, Table table) + -> decltype(blank_update_t().single_table(table)) + { + static_assert(std::is_base_of::value, "Invalid database parameter"); + return {blank_update_t().single_table(table)}; + } + } +} + +#endif diff --git a/include/sqlpp11/postgresql/visibility.h b/include/sqlpp11/postgresql/visibility.h new file mode 100644 index 00000000..0da2365a --- /dev/null +++ b/include/sqlpp11/postgresql/visibility.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015 cszawisza + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP11_POSTGRESQL_VISIBILITY_H +#define SQLPP11_POSTGRESQL_VISIBILITY_H + +#ifdef SQLPP_DYNAMIC_LOADING +#if defined _WIN32 || defined __CYGWIN__ +#ifdef BUILDING_DLL +#ifdef __GNUC__ +#define DLL_PUBLIC __attribute__((dllexport)) +#else +#define DLL_PUBLIC __declspec(dllexport) // Note: actually gcc seems to also supports this syntax. +#endif +#else +#ifdef __GNUC__ +#define DLL_PUBLIC __attribute__((dllimport)) +#else +#define DLL_PUBLIC __declspec(dllimport) // Note: actually gcc seems to also supports this syntax. +#endif +#endif +#define DLL_LOCAL +#else +#if __GNUC__ >= 4 +#define DLL_PUBLIC __attribute__((visibility("default"))) +#define DLL_LOCAL __attribute__((visibility("hidden"))) +#else +#define DLL_PUBLIC +#define DLL_LOCAL +#endif +#endif +#else +#define DLL_PUBLIC +#define DLL_LOCAL +#endif + +#endif diff --git a/include/sqlpp11/select_column_list.h b/include/sqlpp11/select_column_list.h index 5d13c071..20a827b0 100644 --- a/include/sqlpp11/select_column_list.h +++ b/include/sqlpp11/select_column_list.h @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -291,24 +292,6 @@ namespace sqlpp namespace detail { - template - std::tuple> as_column_tuple(T t) - { - return std::tuple>(auto_alias_t{t}); - } - - template - std::tuple...> as_column_tuple(std::tuple t) - { - return t; - } - - template - auto column_tuple_merge(Columns... columns) -> decltype(std::tuple_cat(as_column_tuple(columns)...)) - { - return std::tuple_cat(as_column_tuple(columns)...); - } - template select_column_list_t make_column_list(std::tuple columns); } // namespace detail diff --git a/tests/postgresql/CMakeLists.txt b/tests/postgresql/CMakeLists.txt new file mode 100644 index 00000000..61677a07 --- /dev/null +++ b/tests/postgresql/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright (c) 2021-2021, Roland Bock +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this +# list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +add_subdirectory(constraints) +add_subdirectory(usage) + + diff --git a/tests/postgresql/constraints/CMakeLists.txt b/tests/postgresql/constraints/CMakeLists.txt new file mode 100644 index 00000000..96a3d03f --- /dev/null +++ b/tests/postgresql/constraints/CMakeLists.txt @@ -0,0 +1,53 @@ +# Copyright (c) 2013-2015, Roland Bock +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, this +# list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +function(test_constraint name pattern) + set(test sqlpp11.postgresql.constraints.${name}) + set(target sqlpp11_postgresql_${name}) + add_executable(${target} EXCLUDE_FROM_ALL ${name}.cpp) + target_link_libraries(${target} PRIVATE sqlpp11 sqlpp11_testing) + target_link_libraries(${target} PRIVATE PostgreSQL::PostgreSQL) + add_test(NAME ${test} + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target ${target} + ) + set_property(TEST ${test} PROPERTY PASS_REGULAR_EXPRESSION ${pattern}) + # conditionally bump to a higher C++ standard to test compatibility + if (SQLPP11_TESTS_CXX_STD) + set_property(TARGET ${target} PROPERTY CXX_STANDARD ${SQLPP11_TESTS_CXX_STD}) + set_property(TARGET ${target} PROPERTY CXX_STANDARD_REQUIRED yes) + set_property(TARGET ${target} PROPERTY CXX_EXTENSIONS no) + endif() +endfunction() + +# Compiling these is required to fail (testing some static_assert) +test_constraint(OnConflictEmptyWhereDoUpdate "conflict_target specification is required with do_update") +test_constraint(OnConflictInvalidAssignmentsDoUpdate "conflict_target specification is required with do_update") +test_constraint(OnConflictInvalidParameter "only a column is supported as conflict_target specification") +test_constraint(OnConflictInvalidWhereDoUpdate "conflict_target specification is required with do_update") +test_constraint(OnConflictMissingAction "either do_nothing\\(\\) or do_update\\(...\\) is required with on_conflict") +test_constraint(OnConflictMissingAssignmentsDoUpdate "conflict_target specification is required with do_update") +test_constraint(OnConflictMissingParameterDoUpdate "conflict_target specification is required with do_update") +test_constraint(ReturningEmptyAssert "at least one returnable expression") +test_constraint(ReturningInvalidArgument "at least one returning column requires a table which is otherwise not known in the statement") + diff --git a/tests/postgresql/constraints/OnConflictEmptyWhereDoUpdate.cpp b/tests/postgresql/constraints/OnConflictEmptyWhereDoUpdate.cpp new file mode 100644 index 00000000..da037831 --- /dev/null +++ b/tests/postgresql/constraints/OnConflictEmptyWhereDoUpdate.cpp @@ -0,0 +1,47 @@ +/** + * Copyright © 2014-2019, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "../usage/TabFoo.h" + +namespace sql = sqlpp::postgresql; + +int main(int, char*[]) +{ + model::TabFoo foo = {}; + // model::TabBar bar = {}; + + auto insert1 = sql::insert_into(foo) + .default_values() + .on_conflict() + .do_update(foo.beta = 5, foo.gamma = "test bla", foo.c_bool = true) + .where(); +} diff --git a/tests/postgresql/constraints/OnConflictInvalidAssignmentsDoUpdate.cpp b/tests/postgresql/constraints/OnConflictInvalidAssignmentsDoUpdate.cpp new file mode 100644 index 00000000..c4f94494 --- /dev/null +++ b/tests/postgresql/constraints/OnConflictInvalidAssignmentsDoUpdate.cpp @@ -0,0 +1,48 @@ +/** + * Copyright © 2014-2019, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "../usage/TabBar.h" +#include "../usage/TabFoo.h" + +namespace sql = sqlpp::postgresql; + +int main(int, char*[]) +{ + model::TabFoo foo = {}; + model::TabBar bar = {}; + + auto insert1 = sql::insert_into(foo) + .default_values() + .on_conflict() + .do_update(foo.beta = 3, bar.c_int = 5) + .where(foo.beta == 2); +} diff --git a/tests/postgresql/constraints/OnConflictInvalidParameter.cpp b/tests/postgresql/constraints/OnConflictInvalidParameter.cpp new file mode 100644 index 00000000..218f0158 --- /dev/null +++ b/tests/postgresql/constraints/OnConflictInvalidParameter.cpp @@ -0,0 +1,44 @@ +/** + * Copyright © 2014-2019, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "../usage/TabBar.h" +#include "../usage/TabFoo.h" + +namespace sql = sqlpp::postgresql; + +int main(int, char*[]) +{ + model::TabFoo foo = {}; + // model::TabBar bar = {}; + + auto insert1 = sql::insert_into(foo).default_values().on_conflict(foo).do_nothing(); +} diff --git a/tests/postgresql/constraints/OnConflictInvalidWhereDoUpdate.cpp b/tests/postgresql/constraints/OnConflictInvalidWhereDoUpdate.cpp new file mode 100644 index 00000000..17389592 --- /dev/null +++ b/tests/postgresql/constraints/OnConflictInvalidWhereDoUpdate.cpp @@ -0,0 +1,48 @@ +/** + * Copyright © 2014-2019, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "../usage/TabBar.h" +#include "../usage/TabFoo.h" + +namespace sql = sqlpp::postgresql; + +int main(int, char*[]) +{ + model::TabFoo foo = {}; + // model::TabBar bar = {}; + + auto insert1 = sql::insert_into(foo) + .default_values() + .on_conflict() + .do_update(foo.beta = 5, foo.gamma = "test bla", foo.c_bool = true) + .where(foo.beta = 3); +} diff --git a/tests/postgresql/constraints/OnConflictMissingAction.cpp b/tests/postgresql/constraints/OnConflictMissingAction.cpp new file mode 100644 index 00000000..328696f9 --- /dev/null +++ b/tests/postgresql/constraints/OnConflictMissingAction.cpp @@ -0,0 +1,46 @@ +/** + * Copyright © 2014-2019, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "../usage/TabBar.h" +#include "../usage/TabFoo.h" + +namespace sql = sqlpp::postgresql; + +int main(int, char*[]) +{ + model::TabFoo foo = {}; + // model::TabBar bar = {}; + + auto sql = sql::insert_into(foo).default_values().on_conflict(foo.beta); + sql::connection dummydb; + const auto res = dummydb(sql); +} diff --git a/tests/postgresql/constraints/OnConflictMissingAssignmentsDoUpdate.cpp b/tests/postgresql/constraints/OnConflictMissingAssignmentsDoUpdate.cpp new file mode 100644 index 00000000..06b8f23a --- /dev/null +++ b/tests/postgresql/constraints/OnConflictMissingAssignmentsDoUpdate.cpp @@ -0,0 +1,48 @@ +/** + * Copyright © 2014-2019, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "../usage/TabBar.h" +#include "../usage/TabFoo.h" + +namespace sql = sqlpp::postgresql; + +int main(int, char*[]) +{ + model::TabFoo foo = {}; + // model::TabBar bar = {}; + + auto insert1 = sql::insert_into(foo) + .default_values() + .on_conflict() + .do_update() + .where(foo.beta == 2); +} diff --git a/tests/postgresql/constraints/OnConflictMissingParameterDoUpdate.cpp b/tests/postgresql/constraints/OnConflictMissingParameterDoUpdate.cpp new file mode 100644 index 00000000..53294db5 --- /dev/null +++ b/tests/postgresql/constraints/OnConflictMissingParameterDoUpdate.cpp @@ -0,0 +1,48 @@ +/** + * Copyright © 2014-2019, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "../usage/TabBar.h" +#include "../usage/TabFoo.h" + +namespace sql = sqlpp::postgresql; + +int main(int, char*[]) +{ + model::TabFoo foo = {}; + // model::TabBar bar = {}; + + auto insert1 = sql::insert_into(foo) + .default_values() + .on_conflict() + .do_update(foo.beta = 5, foo.gamma = "test bla", foo.c_bool = true) + .where(foo.beta == 2); +} diff --git a/tests/postgresql/constraints/ReturningEmptyAssert.cpp b/tests/postgresql/constraints/ReturningEmptyAssert.cpp new file mode 100644 index 00000000..f72dc462 --- /dev/null +++ b/tests/postgresql/constraints/ReturningEmptyAssert.cpp @@ -0,0 +1,57 @@ +/** + * Copyright © 2014-2019, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include + +#include +#include + +#include "../usage/TabBar.h" +#include "../usage/TabFoo.h" + +namespace sql = sqlpp::postgresql; +int main(int, char*[]) +{ + // Tables + model::TabFoo foo; + model::TabBar bar; + + // Should not compile + auto sql = sqlpp::postgresql::insert_into(foo).set(foo.gamma = "bla").returning(); + MockDb dummydb; + const auto res = dummydb(sql); + + //auto sql = sqlpp::postgresql::insert_into(foo).set(foo.gamma = "bla").returning(all_of(foo)); + + //auto sql = sqlpp::postgresql::insert_into(foo).set(foo.gamma = "bla").returning(bar.c_int); + //MockDb dummydb; + //const auto res = dummydb(sql); + + // Should not compile + //auto sql = sqlpp::postgresql::insert_into(foo).set(foo.gamma = "bla").returning(); + + return 0; +} diff --git a/tests/postgresql/constraints/ReturningInvalidArgument.cpp b/tests/postgresql/constraints/ReturningInvalidArgument.cpp new file mode 100644 index 00000000..d8a45b92 --- /dev/null +++ b/tests/postgresql/constraints/ReturningInvalidArgument.cpp @@ -0,0 +1,48 @@ +/** + * Copyright © 2014-2019, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include + +#include +#include + +#include "../usage/TabBar.h" +#include "../usage/TabFoo.h" + +namespace sql = sqlpp::postgresql; +int main(int, char*[]) +{ + // Tables + model::TabFoo foo; + model::TabBar bar; + + // Fail to compile + auto sql = sqlpp::postgresql::insert_into(foo).set(foo.gamma = "bla").returning(foo.gamma, bar.c_int); + sql::connection dummydb; + const auto res = dummydb(sql); + + return 0; +} diff --git a/tests/postgresql/usage/Basic.cpp b/tests/postgresql/usage/Basic.cpp new file mode 100644 index 00000000..e3c8c93b --- /dev/null +++ b/tests/postgresql/usage/Basic.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017, Volker Aßmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +namespace sql = sqlpp::postgresql; +int Basic(int, char*[]) +{ + auto config = std::make_shared(); + config->host = "localhost"; + config->user = "unknown_user_must_fail"; + try + { + sql::connection db(config); + + throw std::logic_error("should never reach this point"); + } + catch (const sqlpp::postgresql::broken_connection& ex) + { + std::cout << "Got exception: '" << ex.what() << "'"; + return 0; + } + + return 1; +} diff --git a/tests/postgresql/usage/CMakeLists.txt b/tests/postgresql/usage/CMakeLists.txt new file mode 100644 index 00000000..6abf1009 --- /dev/null +++ b/tests/postgresql/usage/CMakeLists.txt @@ -0,0 +1,64 @@ +# Copyright (c) 2015, Matthijs Möhlmann +# Copyright (c) 2021, Roland Bock +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, this +# list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +add_library(sqlpp11_postgresql_testing INTERFACE) +target_include_directories(sqlpp11_postgresql_testing INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +set(test_names + Basic + Constructor + Date + DateTime + Exceptions + InsertOnConflict + Returning + Select + Transaction + Type + ) + +create_test_sourcelist(test_sources test_main.cpp ${test_names}) +add_executable(sqlpp11_postgresql_tests ${test_sources}) +target_link_libraries(sqlpp11_postgresql_tests PRIVATE sqlpp11 sqlpp11_testing sqlpp11_postgresql_testing) +target_link_libraries(sqlpp11_postgresql_tests PRIVATE sqlpp11::sqlpp11) +target_link_libraries(sqlpp11_postgresql_tests PRIVATE PostgreSQL::PostgreSQL) +target_link_libraries(sqlpp11_postgresql_tests PRIVATE date::date) +if(NOT MSVC) + target_compile_options(sqlpp11_postgresql_tests PRIVATE -Wall -Wextra -pedantic) +endif() + +# conditionally bump to a higher C++ standard to test compatibility +if (SQLPP11_TESTS_CXX_STD) +set_property(TARGET sqlpp11_postgresql_tests PROPERTY CXX_STANDARD ${SQLPP11_TESTS_CXX_STD}) +set_property(TARGET sqlpp11_postgresql_tests PROPERTY CXX_STANDARD_REQUIRED yes) + set_property(TARGET sqlpp11_postgresql_tests PROPERTY CXX_EXTENSIONS no) +endif() + +foreach(test IN LISTS test_names) + add_test(NAME sqlpp11.postgresql.usage.${test} + COMMAND sqlpp11_postgresql_tests ${test} + ) +endforeach() + diff --git a/tests/postgresql/usage/Constructor.cpp b/tests/postgresql/usage/Constructor.cpp new file mode 100644 index 00000000..bdf52332 --- /dev/null +++ b/tests/postgresql/usage/Constructor.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017, Serge Robyns + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace sql = sqlpp::postgresql; + +int Constructor(int, char*[]) +{ + sql::connection db; + return 0; +} diff --git a/tests/postgresql/usage/Date.cpp b/tests/postgresql/usage/Date.cpp new file mode 100644 index 00000000..5cd40980 --- /dev/null +++ b/tests/postgresql/usage/Date.cpp @@ -0,0 +1,146 @@ +/** + * Copyright © 2017 Volker Aßmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include "TabSample.h" +#include "make_test_connection.h" + +namespace +{ + const auto now = ::sqlpp::chrono::floor<::std::chrono::microseconds>(std::chrono::system_clock::now()); + const auto today = ::sqlpp::chrono::floor<::sqlpp::chrono::days>(now); + const auto yesterday = today - ::sqlpp::chrono::days{1}; + + template + void require_equal(int line, const L& l, const R& r) + { + if (l != r) + { + std::cerr << line << ": "; + serialize(::sqlpp::wrap_operand_t{l}, std::cerr); + std::cerr << " != "; + serialize(::sqlpp::wrap_operand_t{r}, std::cerr); + throw std::runtime_error("Unexpected result"); + } + } + + template + void prepare_table(Db&& db, bool with_tz) + { + db.execute("DROP TABLE IF EXISTS tab_date_time"); + if (with_tz) + { + // prepare test with timezone + db.execute("CREATE TABLE tab_date_time (col_day_point DATE, col_time_point TIMESTAMP WITH TIME ZONE)"); + } + else + { + // prepare test without timezone + db.execute("CREATE TABLE tab_date_time (col_day_point DATE, col_time_point TIMESTAMP)"); + } + } + +} // namespace + +int Date(int, char*[]) +{ + + namespace sql = sqlpp::postgresql; + + sql::connection db = sql::make_test_connection(); + + + try + { + for (int i = 0; i <= 1; ++i) + { + if (i) + std::cerr << "Testing date/time WITH timezone\n"; + else + std::cerr << "Testing date/time WITHOUT timezone\n"; + prepare_table(db, i); + + const auto tab = TabDateTime{}; + db(insert_into(tab).default_values()); + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + require_equal(__LINE__, row.colDayPoint.is_null(), true); + require_equal(__LINE__, row.colDayPoint.value(), ::sqlpp::chrono::day_point{}); + require_equal(__LINE__, row.colTimePoint.is_null(), true); + require_equal(__LINE__, row.colTimePoint.value(), ::sqlpp::chrono::microsecond_point{}); + } + + db(update(tab).set(tab.colDayPoint = today, tab.colTimePoint = now).unconditionally()); + + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + require_equal(__LINE__, row.colDayPoint.value(), today); + require_equal(__LINE__, row.colTimePoint.value(), now); + } + + db(update(tab).set(tab.colDayPoint = yesterday, tab.colTimePoint = today).unconditionally()); + + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + require_equal(__LINE__, row.colDayPoint.value(), yesterday); + require_equal(__LINE__, row.colTimePoint.value(), today); + } + + auto prepared_update = db.prepare( + update(tab) + .set(tab.colDayPoint = parameter(tab.colDayPoint), tab.colTimePoint = parameter(tab.colTimePoint)) + .unconditionally()); + prepared_update.params.colDayPoint = today; + prepared_update.params.colTimePoint = now; + std::cout << "---- running prepared update ----" << std::endl; + db(prepared_update); + std::cout << "---- finished prepared update ----" << std::endl; + + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + require_equal(__LINE__, row.colDayPoint.value(), today); + require_equal(__LINE__, row.colTimePoint.value(), now); + } + } + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 1; + } + catch (...) + { + std::cerr << "Unknown exception" << std::endl; + return 1; + } + return 0; +} diff --git a/tests/postgresql/usage/DateTime.cpp b/tests/postgresql/usage/DateTime.cpp new file mode 100644 index 00000000..29541801 --- /dev/null +++ b/tests/postgresql/usage/DateTime.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2013 - 2015, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include "TabFoo.h" +#include "make_test_connection.h" + +namespace +{ + const auto now = ::date::floor<::std::chrono::microseconds>(std::chrono::system_clock::now()); + const auto today = ::date::floor<::sqlpp::chrono::days>(now); + const auto yesterday = today - ::sqlpp::chrono::days{1}; + + template + auto require_equal(int line, const L& l, const R& r) -> void + { + if (l != r) + { + std::cerr << line << ": "; + serialize(::sqlpp::wrap_operand_t{l}, std::cerr); + std::cerr << " != "; + serialize(::sqlpp::wrap_operand_t{r}, std::cerr); + throw std::runtime_error("Unexpected result"); + } + } +} // namespace + +int DateTime(int, char*[]) +{ + namespace sql = sqlpp::postgresql; + + sql::connection db = sql::make_test_connection(); + + db.execute(R"(DROP TABLE IF EXISTS tabfoo;)"); + db.execute(R"(CREATE TABLE tabfoo + ( + alpha bigserial NOT NULL, + beta smallint, + gamma text, + c_bool boolean, + c_timepoint timestamp with time zone, + c_day date + ))"); + + model::TabFoo tab = {}; + try + { + db(insert_into(tab).default_values()); + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + require_equal(__LINE__, row.c_day.is_null(), true); + require_equal(__LINE__, row.c_day.value(), ::sqlpp::chrono::day_point{}); + require_equal(__LINE__, row.c_timepoint.is_null(), true); + require_equal(__LINE__, row.c_timepoint.value(), ::sqlpp::chrono::microsecond_point{}); + } + + db(update(tab).set(tab.c_day = today, tab.c_timepoint = now).unconditionally()); + + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + require_equal(__LINE__, row.c_day.value(), today); + require_equal(__LINE__, row.c_timepoint.value(), now); + } + + db(update(tab).set(tab.c_day = yesterday, tab.c_timepoint = today).unconditionally()); + + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + require_equal(__LINE__, row.c_day.value(), yesterday); + require_equal(__LINE__, row.c_timepoint.value(), today); + } + + auto prepared_update = + db.prepare(update(tab) + .set(tab.c_day = parameter(tab.c_day), tab.c_timepoint = parameter(tab.c_timepoint)) + .unconditionally()); + prepared_update.params.c_day = today; + prepared_update.params.c_timepoint = now; + std::cout << "---- running prepared update ----" << std::endl; + db(prepared_update); + std::cout << "---- finished prepared update ----" << std::endl; + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + require_equal(__LINE__, row.c_day.value(), today); + require_equal(__LINE__, row.c_timepoint.value(), now); + } + } + catch (const sql::failure& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 1; + } + + return 0; +} diff --git a/tests/postgresql/usage/Exceptions.cpp b/tests/postgresql/usage/Exceptions.cpp new file mode 100644 index 00000000..512b5b97 --- /dev/null +++ b/tests/postgresql/usage/Exceptions.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014-2015, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "assertThrow.h" + +#include "TabBar.h" +#include "TabFoo.h" +#include "make_test_connection.h" + +namespace sql = sqlpp::postgresql; +int Exceptions(int, char*[]) +{ + { + // broken_connection exception on bad config + auto config = std::make_shared(); + config->host = "non-existing-host"; + assert_throw(sql::connection db(config), sql::broken_connection); + } + + model::TabFoo foo = {}; + sql::connection db = sql::make_test_connection(); + + try + { + db.execute(R"(DROP TABLE IF EXISTS tabfoo;)"); + db.execute(R"(CREATE TABLE tabfoo + ( + alpha bigserial NOT NULL, + beta smallint UNIQUE, + gamma text CHECK( length(gamma) < 5 ), + c_bool boolean, + c_timepoint timestamp with time zone DEFAULT now(), + c_day date + ))"); + + assert_throw(db(insert_into(foo).set(foo.beta = std::numeric_limits::max() + 1)), sql::data_exception); + assert_throw(db(insert_into(foo).set(foo.gamma = "123456")), sql::check_violation); + db(insert_into(foo).set(foo.beta = 5)); + assert_throw(db(insert_into(foo).set(foo.beta = 5)), sql::integrity_constraint_violation); + assert_throw(db.last_insert_id("tabfoo", "no_such_column"), sqlpp::postgresql::undefined_table); + } + catch (const sql::failure& e) + { + std::cout << e.what(); + return 1; + } + + return 0; +} diff --git a/tests/postgresql/usage/InsertOnConflict.cpp b/tests/postgresql/usage/InsertOnConflict.cpp new file mode 100644 index 00000000..af4cc616 --- /dev/null +++ b/tests/postgresql/usage/InsertOnConflict.cpp @@ -0,0 +1,89 @@ +/** + * Copyright © 2014-2019, Matthijs Möhlmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "TabBar.h" +#include "TabFoo.h" +#include "make_test_connection.h" + +namespace sql = sqlpp::postgresql; + +int InsertOnConflict(int, char*[]) +{ + model::TabFoo foo = {}; + + sql::connection db = sql::make_test_connection(); + + db.execute(R"(DROP TABLE IF EXISTS tabfoo;)"); + db.execute(R"(CREATE TABLE tabfoo + ( + alpha bigserial PRIMARY KEY NOT NULL, + beta smallint, + gamma text, + c_bool boolean, + c_timepoint timestamp with time zone, + c_day date + ))"); + +#warning: Need to add serialization tests + // Test on conflict + db(sql::insert_into(foo).default_values().on_conflict().do_nothing()); + + // Test on conflict (with conflict target) + db(sql::insert_into(foo).default_values().on_conflict(foo.alpha).do_nothing()); + + // Conflict target + db(sql::insert_into(foo).default_values().on_conflict(foo.alpha).do_update( + foo.beta = 5, foo.gamma = "test bla", foo.c_bool = true)); + + // With where statement + for (const auto& row : db(sql::insert_into(foo) + .default_values() + .on_conflict(foo.alpha) + .do_update(foo.beta = 5, foo.gamma = "test bla", foo.c_bool = true) + .where(foo.beta == 2) + .returning(foo.gamma))) + { + std::cout << row.gamma << std::endl; + } + + // Returning + for (const auto& row : db(sql::insert_into(foo) + .default_values() + .on_conflict(foo.alpha) + .do_update(foo.beta = 5, foo.gamma = "test bla", foo.c_bool = true) + .returning(foo.beta))) + { + std::cout << row.beta << std::endl; + } + + return 0; +} diff --git a/tests/postgresql/usage/Returning.cpp b/tests/postgresql/usage/Returning.cpp new file mode 100644 index 00000000..a3cf5b05 --- /dev/null +++ b/tests/postgresql/usage/Returning.cpp @@ -0,0 +1,64 @@ +#include + +#include +#include + +#include "TabFoo.h" +#include "make_test_connection.h" + +int Returning(int, char*[]) +{ + namespace sql = sqlpp::postgresql; + + sql::connection db = sql::make_test_connection(); + + model::TabFoo foo = {}; + + try + { + db.execute(R"(DROP TABLE IF EXISTS tabfoo;)"); + db.execute(R"(CREATE TABLE tabfoo + ( + alpha bigserial NOT NULL, + beta smallint, + gamma text, + c_bool boolean, + c_timepoint timestamp with time zone DEFAULT now(), + c_day date + ))"); + + std::cout + << db(sqlpp::postgresql::insert_into(foo).set(foo.gamma = "dsa").returning(foo.c_timepoint)).front().c_timepoint + << std::endl; + + std::cout + << db(sqlpp::postgresql::insert_into(foo).set(foo.gamma = "asd").returning(std::make_tuple(foo.c_timepoint))).front().c_timepoint + << std::endl; + + auto i = sqlpp::postgresql::dynamic_insert_into(db, foo).dynamic_set().returning(foo.c_timepoint); + i.insert_list.add(foo.gamma = "blah"); + + std::cout << db(i).front().c_timepoint << std::endl; + + auto updated = + db(sqlpp::postgresql::update(foo).set(foo.beta = 0).unconditionally().returning(foo.gamma, foo.beta)); + for (const auto& row : updated) + std::cout << "Gamma: " << row.gamma << " Beta: " << row.beta << std::endl; + + auto multi_insert = sqlpp::postgresql::insert_into(foo).columns(foo.beta).returning(foo.alpha, foo.beta); + multi_insert.values.add(foo.beta = 1); + multi_insert.values.add(foo.beta = 2); + auto inserted = db(multi_insert); + + for (const auto& row : inserted) + std::cout << row.beta << std::endl; + +} + + catch (const sql::failure&) + { + return 1; + } + + return 0; +} diff --git a/tests/postgresql/usage/Select.cpp b/tests/postgresql/usage/Select.cpp new file mode 100644 index 00000000..81a4c025 --- /dev/null +++ b/tests/postgresql/usage/Select.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2013 - 2015, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include + +#include "TabFoo.h" +#include "make_test_connection.h" + +SQLPP_ALIAS_PROVIDER(left); + +namespace sql = sqlpp::postgresql; +model::TabFoo tab = {}; + +void testSelectAll(sql::connection& db, int expectedRowCount) +{ + std::cerr << "--------------------------------------" << std::endl; + int i = 0; + for (const auto& row : db(sqlpp::select(all_of(tab)).from(tab).unconditionally())) + { + ++i; + std::cerr << ">>> row.alpha: " << row.alpha << ", row.beta: " << row.beta << ", row.gamma: " << row.gamma + << std::endl; + assert(i == row.alpha); + }; + assert(i == expectedRowCount); + + auto preparedSelectAll = db.prepare(sqlpp::select(all_of(tab)).from(tab).unconditionally()); + i = 0; + for (const auto& row : db(preparedSelectAll)) + { + ++i; + std::cerr << ">>> row.alpha: " << row.alpha << ", row.beta: " << row.beta << ", row.gamma: " << row.gamma + << std::endl; + assert(i == row.alpha); + }; + assert(i == expectedRowCount); + std::cerr << "--------------------------------------" << std::endl; +} + +int Select(int, char*[]) +{ + sql::connection db = sql::make_test_connection(); + + db.execute(R"(DROP TABLE IF EXISTS tabfoo;)"); + db.execute(R"(CREATE TABLE tabfoo + ( + alpha bigserial NOT NULL, + beta smallint, + gamma text, + c_bool boolean, + c_timepoint timestamp with time zone, + c_day date + ))"); + + testSelectAll(db, 0); + db(insert_into(tab).default_values()); + testSelectAll(db, 1); + db(insert_into(tab).set(tab.c_bool = true, tab.gamma = "cheesecake")); + testSelectAll(db, 2); + db(insert_into(tab).set(tab.c_bool = true, tab.gamma = "cheesecake")); + testSelectAll(db, 3); + + // Test size functionality + const auto test_size = db(select(all_of(tab)).from(tab).unconditionally()); + assert(test_size.size() == 3); + + // test functions and operators + db(select(all_of(tab)).from(tab).where(tab.alpha.is_null())); + db(select(all_of(tab)).from(tab).where(tab.alpha.is_not_null())); + db(select(all_of(tab)).from(tab).where(tab.alpha.in(1, 2, 3))); + db(select(all_of(tab)).from(tab).where(tab.alpha.in(sqlpp::value_list(std::vector{1, 2, 3, 4})))); + db(select(all_of(tab)).from(tab).where(tab.alpha.not_in(1, 2, 3))); + db(select(all_of(tab)).from(tab).where(tab.alpha.not_in(sqlpp::value_list(std::vector{1, 2, 3, 4})))); + db(select(count(tab.alpha)).from(tab).unconditionally()); + db(select(avg(tab.alpha)).from(tab).unconditionally()); + db(select(max(tab.alpha)).from(tab).unconditionally()); + db(select(min(tab.alpha)).from(tab).unconditionally()); + db(select(exists(select(tab.alpha).from(tab).where(tab.alpha > 7))).from(tab).unconditionally()); + db(select(all_of(tab)).from(tab).where(tab.alpha == any(select(tab.alpha).from(tab).where(tab.alpha < 3)))); + db(select(all_of(tab)).from(tab).where(tab.alpha == some(select(tab.alpha).from(tab).where(tab.alpha < 3)))); + db(select(all_of(tab)).from(tab).where(tab.alpha + tab.alpha > 3)); + db(select(all_of(tab)).from(tab).where((tab.gamma + tab.gamma) == "")); + db(select(all_of(tab)).from(tab).where((tab.gamma + tab.gamma).like("%'\"%"))); + + // test boolean value + db(insert_into(tab).set(tab.c_bool = true, tab.gamma = "asdf")); + db(insert_into(tab).set(tab.c_bool = false, tab.gamma = "asdfg")); + + assert(db(select(tab.c_bool).from(tab).where(tab.gamma == "asdf")).front().c_bool); + assert(not db(select(tab.c_bool).from(tab).where(tab.gamma == "asdfg")).front().c_bool); + assert(not db(select(tab.c_bool).from(tab).where(tab.alpha == 1)).front().c_bool); + + // test + + // update + db(update(tab).set(tab.c_bool = false).where(tab.alpha.in(1))); + db(update(tab).set(tab.c_bool = false).where(tab.alpha.in(sqlpp::value_list(std::vector{1, 2, 3, 4})))); + + // remove + db(remove_from(tab).where(tab.alpha == tab.alpha + 3)); + + auto result = db(select(all_of(tab)).from(tab).unconditionally()); + std::cerr << "Accessing a field directly from the result (using the current row): " << result.begin()->alpha + << std::endl; + std::cerr << "Can do that again, no problem: " << result.begin()->alpha << std::endl; + + auto tx = start_transaction(db); + if (const auto& row = *db(select(all_of(tab), select(max(tab.alpha)).from(tab)).from(tab).unconditionally()).begin()) + { + auto a = row.alpha; + auto m = row.max; + std::cerr << "-----------------------------" << a << ", " << m << std::endl; + } + tx.commit(); + + return 0; +} diff --git a/tests/postgresql/usage/TabBar.h b/tests/postgresql/usage/TabBar.h new file mode 100644 index 00000000..e9c299af --- /dev/null +++ b/tests/postgresql/usage/TabBar.h @@ -0,0 +1,61 @@ +#ifndef MODEL_TABBAR_H +#define MODEL_TABBAR_H + +#include +#include +#include + +namespace model +{ + namespace TabBar_ + { + struct C_int + { + struct _alias_t + { + static constexpr const char _literal[] = "c_int"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T c_int; + T& operator()() + { + return c_int; + } + const T& operator()() const + { + return c_int; + } + }; + }; + + using _traits = ::sqlpp::make_traits<::sqlpp::bigint, sqlpp::tag::can_be_null>; + }; + } + + struct TabBar : sqlpp::table_t + { + using _value_type = sqlpp::no_value_t; + struct _alias_t + { + static constexpr const char _literal[] = "TabBar"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T TabBar; + T& operator()() + { + return TabBar; + } + const T& operator()() const + { + return TabBar; + } + }; + }; + }; +} + +#endif diff --git a/tests/postgresql/usage/TabFoo.h b/tests/postgresql/usage/TabFoo.h new file mode 100644 index 00000000..95cbe9a1 --- /dev/null +++ b/tests/postgresql/usage/TabFoo.h @@ -0,0 +1,187 @@ +#ifndef MODEL_TABFOO_H +#define MODEL_TABFOO_H + +#include +#include +#include + +namespace model +{ + namespace TabFoo_ + { + struct Alpha + { + struct _alias_t + { + static constexpr const char _literal[] = "alpha"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T alpha; + T& operator()() + { + return alpha; + } + const T& operator()() const + { + return alpha; + } + }; + }; + + using _traits = ::sqlpp::make_traits<::sqlpp::bigint, sqlpp::tag::can_be_null>; + }; + + struct Beta + { + struct _alias_t + { + static constexpr const char _literal[] = "beta"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T beta; + T& operator()() + { + return beta; + } + const T& operator()() const + { + return beta; + } + }; + }; + + using _traits = ::sqlpp::make_traits<::sqlpp::smallint, sqlpp::tag::can_be_null>; + }; + + struct Gamma + { + struct _alias_t + { + static constexpr const char _literal[] = "gamma"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T gamma; + T& operator()() + { + return gamma; + } + const T& operator()() const + { + return gamma; + } + }; + }; + + using _traits = ::sqlpp::make_traits<::sqlpp::text, sqlpp::tag::can_be_null>; + }; + + struct C_bool + { + struct _alias_t + { + static constexpr const char _literal[] = "c_bool"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T c_bool; + T& operator()() + { + return c_bool; + } + const T& operator()() const + { + return c_bool; + } + }; + }; + + using _traits = ::sqlpp::make_traits<::sqlpp::boolean, sqlpp::tag::can_be_null>; + }; + + struct C_timepoint + { + struct _alias_t + { + static constexpr const char _literal[] = "c_timepoint"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T c_timepoint; + T& operator()() + { + return c_timepoint; + } + const T& operator()() const + { + return c_timepoint; + } + }; + }; + + using _traits = ::sqlpp::make_traits<::sqlpp::time_point, sqlpp::tag::can_be_null>; + }; + + struct C_day + { + struct _alias_t + { + static constexpr const char _literal[] = "c_day"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T c_day; + T& operator()() + { + return c_day; + } + const T& operator()() const + { + return c_day; + } + }; + }; + + using _traits = ::sqlpp::make_traits<::sqlpp::day_point, sqlpp::tag::can_be_null>; + }; + } + + struct TabFoo : sqlpp::table_t + { + using _value_type = sqlpp::no_value_t; + struct _alias_t + { + static constexpr const char _literal[] = "TabFoo"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T TabFoo; + T& operator()() + { + return TabFoo; + } + const T& operator()() const + { + return TabFoo; + } + }; + }; + }; +} + +#endif diff --git a/tests/postgresql/usage/TabSample.h b/tests/postgresql/usage/TabSample.h new file mode 100644 index 00000000..54256ae5 --- /dev/null +++ b/tests/postgresql/usage/TabSample.h @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2013 - 2015, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_TAB_SAMPLE_H +#define SQLPP_TAB_SAMPLE_H + +#include +#include +#include + +namespace TabFoo_ +{ + struct Omega + { + struct _alias_t + { + static constexpr const char _literal[] = "omega"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T omega; + T& operator()() + { + return omega; + } + const T& operator()() const + { + return omega; + } + }; + }; + using _traits = ::sqlpp::make_traits<::sqlpp::bigint>; + }; +} + +struct TabFoo : sqlpp::table_t +{ + using _value_type = sqlpp::no_value_t; + struct _alias_t + { + static constexpr const char _literal[] = "tab_foo"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T tabFoo; + T& operator()() + { + return tabFoo; + } + const T& operator()() const + { + return tabFoo; + } + }; + }; +}; + +namespace TabSample_ +{ + struct Alpha + { + struct _alias_t + { + static constexpr const char _literal[] = "alpha"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T alpha; + T& operator()() + { + return alpha; + } + const T& operator()() const + { + return alpha; + } + }; + }; + using _traits = ::sqlpp::make_traits<::sqlpp::bigint, + ::sqlpp::tag::can_be_null>; + }; + + struct Beta + { + struct _alias_t + { + static constexpr const char _literal[] = "beta"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T beta; + T& operator()() + { + return beta; + } + const T& operator()() const + { + return beta; + } + }; + }; + using _traits = ::sqlpp::make_traits<::sqlpp::varchar, ::sqlpp::tag::can_be_null>; + }; + + struct Gamma + { + struct _alias_t + { + static constexpr const char _literal[] = "gamma"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T gamma; + T& operator()() + { + return gamma; + } + const T& operator()() const + { + return gamma; + } + }; + }; + using _traits = ::sqlpp::make_traits<::sqlpp::boolean>; + }; +} + +struct TabSample : sqlpp::table_t +{ + using _value_type = sqlpp::no_value_t; + struct _alias_t + { + static constexpr const char _literal[] = "tab_sample"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T tabSample; + T& operator()() + { + return tabSample; + } + const T& operator()() const + { + return tabSample; + } + }; + }; +}; + +namespace TabDateTime_ +{ + struct ColDayPoint + { + struct _alias_t + { + static constexpr const char _literal[] = "col_day_point"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T colDayPoint; + T& operator()() + { + return colDayPoint; + } + const T& operator()() const + { + return colDayPoint; + } + }; + }; + using _traits = sqlpp::make_traits; + }; + struct ColTimePoint + { + struct _alias_t + { + static constexpr const char _literal[] = "col_time_point"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T colTimePoint; + T& operator()() + { + return colTimePoint; + } + const T& operator()() const + { + return colTimePoint; + } + }; + }; + using _traits = sqlpp::make_traits; + }; +} + +struct TabDateTime : sqlpp::table_t +{ + struct _alias_t + { + static constexpr const char _literal[] = "tab_date_time"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T tabDateTime; + T& operator()() + { + return tabDateTime; + } + const T& operator()() const + { + return tabDateTime; + } + }; + }; +}; + +#endif diff --git a/tests/postgresql/usage/Transaction.cpp b/tests/postgresql/usage/Transaction.cpp new file mode 100644 index 00000000..8f856a89 --- /dev/null +++ b/tests/postgresql/usage/Transaction.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2017, Volker Aßmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "make_test_connection.h" + +namespace +{ + std::ostream& operator<<(std::ostream& stream, const sqlpp::isolation_level& level) + { + switch (level) + { + case sqlpp::isolation_level::serializable: + { + stream << "SERIALIZABLE"; + break; + } + case sqlpp::isolation_level::repeatable_read: + { + stream << "REPEATABLE READ"; + break; + } + case sqlpp::isolation_level::read_committed: + { + stream << "READ COMMITTED"; + break; + } + case sqlpp::isolation_level::read_uncommitted: + { + stream << "READ UNCOMMITTED"; + break; + } + case sqlpp::isolation_level::undefined: + { + stream << "BEGIN"; + break; + } + } + + return stream; + } + + template + void require_equal(int line, const L& l, const R& r) + { + if (l != r) + { + std::cerr << line << ": " << l << " != " << r << std::endl; + throw std::runtime_error("Unexpected result"); + } + } +} + +namespace sql = sqlpp::postgresql; + +SQLPP_ALIAS_PROVIDER(level); + +int Transaction(int, char*[]) +{ + sql::connection db = sql::make_test_connection(); + + try + { + + { + auto current_level = db(custom_query(sqlpp::verbatim("show transaction_isolation;")) + .with_result_type_of(select(sqlpp::value("").as(level)))) + .front() + .level; + require_equal(__LINE__, current_level, "read committed"); + std::cerr << "isolation level outside transaction: " << current_level << "\n"; + + auto tx = start_transaction(db, sqlpp::isolation_level::serializable); + + current_level = db(custom_query(sqlpp::verbatim("show transaction_isolation;")) + .with_result_type_of(select(sqlpp::value("").as(level)))) + .front() + .level; + require_equal(__LINE__, current_level, "serializable"); + std::cerr << "isolation level in transaction(serializable) : " << current_level << "\n"; + tx.commit(); + } + + require_equal(__LINE__, db.get_default_isolation_level(), sqlpp::isolation_level::read_committed); + db.set_default_isolation_level(sqlpp::isolation_level::serializable); + require_equal(__LINE__, db.get_default_isolation_level(), sqlpp::isolation_level::serializable); + } + catch (const sqlpp::exception& ex) + { + std::cerr << "Got exception: " << ex.what() << std::endl; + return 1; + } + catch (...) + { + std::cerr << "Got unknown exception" << std::endl; + return 1; + } + return 0; +} diff --git a/tests/postgresql/usage/Type.cpp b/tests/postgresql/usage/Type.cpp new file mode 100644 index 00000000..a243c675 --- /dev/null +++ b/tests/postgresql/usage/Type.cpp @@ -0,0 +1,130 @@ +/** + * Copyright © 2017 Volker Aßmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include "TabSample.h" +#include "make_test_connection.h" + +namespace +{ + template + void require_equal(int line, const L& l, const R& r) + { + if (l != r) + { + std::cerr << line << ": --" << l << " != " << r << "--" << std::endl; + throw std::runtime_error("Unexpected result"); + } + } + + template + void prepare_table(Db&& db) + { + // prepare test with timezone + db.execute("DROP TABLE IF EXISTS tab_sample"); + db.execute("CREATE TABLE tab_sample (alpha bigint, beta text, gamma bool)"); + } +} + +namespace sql = sqlpp::postgresql; + +int Type(int, char*[]) +{ + sql::connection db = sql::make_test_connection(); + + try + { + prepare_table(db); + + const auto tab = TabSample{}; + db(insert_into(tab).default_values()); + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + require_equal(__LINE__, row.alpha.is_null(), true); + require_equal(__LINE__, row.alpha.value(), 0); + require_equal(__LINE__, row.beta.is_null(), true); + require_equal(__LINE__, row.beta.value(), ""); + require_equal(__LINE__, row.gamma.is_null(), true); + require_equal(__LINE__, row.gamma.value(), false); + } + + db(update(tab).set(tab.alpha = 10, tab.beta = "Cookies!", tab.gamma = true).unconditionally()); + + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + require_equal(__LINE__, row.alpha.is_null(), false); + require_equal(__LINE__, row.alpha.value(), 10); + require_equal(__LINE__, row.beta.is_null(), false); + require_equal(__LINE__, row.beta.value(), "Cookies!"); + require_equal(__LINE__, row.gamma.is_null(), false); + require_equal(__LINE__, row.gamma.value(), true); + } + + db(update(tab).set(tab.alpha = 20, tab.beta = "Monster", tab.gamma = false).unconditionally()); + + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + require_equal(__LINE__, row.alpha.value(), 20); + require_equal(__LINE__, row.beta.value(), "Monster"); + require_equal(__LINE__, row.gamma.value(), false); + } + + auto prepared_update = db.prepare( + update(tab) + .set(tab.alpha = parameter(tab.alpha), tab.beta = parameter(tab.beta), tab.gamma = parameter(tab.gamma)) + .unconditionally()); + prepared_update.params.alpha = 30; + prepared_update.params.beta = "IceCream"; + prepared_update.params.gamma = true; + std::cout << "---- running prepared update ----" << std::endl; + db(prepared_update); + std::cout << "---- finished prepared update ----" << std::endl; + + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + require_equal(__LINE__, row.alpha.value(), 30); + require_equal(__LINE__, row.beta.value(), "IceCream"); + require_equal(__LINE__, row.gamma.value(), true); + } + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 1; + } + catch (...) + { + std::cerr << "Unknown exception" << std::endl; + return 1; + } + return 0; +} diff --git a/tests/postgresql/usage/assertThrow.h b/tests/postgresql/usage/assertThrow.h new file mode 100644 index 00000000..1c0d1ed4 --- /dev/null +++ b/tests/postgresql/usage/assertThrow.h @@ -0,0 +1,18 @@ +#ifndef ASSERT_THROW_H +#define ASSERT_THROW_H + +#define assert_throw(code, exception) \ + { \ + bool exceptionThrown = false; \ + try \ + { \ + code; \ + } \ + catch (const exception&) \ + { \ + exceptionThrown = true; \ + } \ + assert(exceptionThrown); \ + } + +#endif diff --git a/tests/postgresql/usage/make_test_connection.h b/tests/postgresql/usage/make_test_connection.h new file mode 100644 index 00000000..335c3285 --- /dev/null +++ b/tests/postgresql/usage/make_test_connection.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP11_TEST_POSTGRESQL_USAGE_CONNECT_H +#define SQLPP11_TEST_POSTGRESQL_USAGE_CONNECT_H + + +#include + +namespace sqlpp +{ + namespace postgresql + { + // Starts a connection and sets the time zone to UTC + inline ::sqlpp::postgresql::connection make_test_connection() + { + namespace sql = sqlpp::postgresql; + + auto config = std::make_shared(); + +#ifdef WIN32 + config->dbname = "test"; + config->user = "test"; + config->debug = true; +#else + config->user = getenv("USER"); + config->dbname = "sqlpp_postgresql"; + config->debug = true; +#endif + + sql::connection db; + try + { + db.connectUsing(config); + } + catch (const sqlpp::exception&) + { + std::cerr << "For testing, you'll need to create a database called '" << config->dbname + << "', accessible by user '" << config->user << "' without a password." << std::endl; + throw; + } + + db.execute(R"(SET TIME ZONE 'UTC';)"); + + return db; + } + } // namespace postgresql +} // namespace sqlpp + +#endif