diff --git a/include/sqlpp11/compat/optional.h b/include/sqlpp11/compat/optional.h new file mode 100644 index 00000000..15deb245 --- /dev/null +++ b/include/sqlpp11/compat/optional.h @@ -0,0 +1,247 @@ +#pragma once + +/* + * Copyright (c) 2024, 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. + */ + +#ifdef _MSVC_LANG +#define CXX_STD_VER _MSVC_LANG +#else +#define CXX_STD_VER __cplusplus +#endif + +#if CXX_STD_VER >= 201703L +#include +namespace sqlpp +{ + template + using optional = std::optional; + + using std::nullopt_t; + using std::nullopt; + + using std::bad_optional_access; +} // namespace sqlpp + +#else // incomplete backport of std::optional + +#include +#include + +namespace sqlpp +{ + class nullopt_t + { + }; + constexpr nullopt_t nullopt; + + class bad_optional_access : public std::exception + { + public: + ~bad_optional_access() override = default; + const char* what() const noexcept override + { + return "bad optional access"; + } + }; + + template + class optional + { + // Unnamed union, injecting members into scope. + union + { + char _nothing; + T _value; + }; + bool _active = false; + + // Placement new + template + void create(Args&&... args) + { + new ((void*)std::addressof(_value)) T(std::forward(args)...); + _active = true; + } + + void destroy() + { + if (_active) + { + _value.~T(); + } + } + + public: + optional() noexcept : _nothing(), _active(false) + { + } + optional(T t) : _active(true) + { + create(std::move(t)); + } + + optional(const optional&) = default; + optional(optional&&) = default; + optional(const nullopt_t&) noexcept + { + } + + optional& operator=(const optional&) = default; + optional& operator=(optional&&) = default; + optional& operator=(const nullopt_t&) noexcept + { + destroy(); + } + + ~optional() { + destroy(); + } + + bool has_value() const + { + return _active; + } + + explicit operator bool() const + { + return _active; + } + + T& operator*() + { + return _value; + } + + const T& operator*() const + { + return _value; + } + + const T& operator->() const + { + return _value; + } + + template + optional& emplace(Args&&... args) { + create(std::forward(args)...); + } + + T& value() + { + if (_active) + return _value; + throw bad_optional_access(); + } + + template + T value_or(U&& u) + { + if (_active) + return _value; + return std::forward(u); + } + + const T& value() const + { + if (_active) + return _value; + throw bad_optional_access(); + } + + void reset() + { + destroy(); + } + }; + + template + bool operator==(const optional& left, const optional& right) + { + if (static_cast(left) != static_cast(right)) + return false; + if (!static_cast(left)) + return true; + return *left == *right; + } + + template + bool operator==(const optional& left, const R& right) + { + if (!static_cast(left)) + return false; + return *left == right; + } + + template + bool operator==(const L& left, const optional& right) + { + if (!static_cast(right)) + return false; + return left == *right; + } + + template + bool operator!=(const optional& left, const optional& right) + { + if (static_cast(left) != static_cast(right)) + return true; + if (!static_cast(left)) + return false; + return *left != *right; + } + + template + bool operator!=(const optional& left, const R& right) + { + if (!static_cast(left)) + return true; + return *left != right; + } + + template + bool operator!=(const L& left, const optional& right) + { + if (!static_cast(right)) + return true; + return left != *right; + } + + template + bool operator==(const optional& left, const nullopt_t&) + { + return !left; + } + + template + bool operator==(const nullopt_t& n, const optional& right) + { + return !right; + } + +} // namespace sqlpp + +#endif diff --git a/include/sqlpp11/data_types/integral/result_field.h b/include/sqlpp11/compat/span.h similarity index 64% rename from include/sqlpp11/data_types/integral/result_field.h rename to include/sqlpp11/compat/span.h index cdd3bae1..22fcbc8f 100644 --- a/include/sqlpp11/data_types/integral/result_field.h +++ b/include/sqlpp11/compat/span.h @@ -1,7 +1,7 @@ #pragma once /* - * Copyright (c) 2013-2015, Roland Bock + * Copyright (c) 2024, Roland Bock * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -26,28 +26,52 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include -#include -#include -#include -#include +#ifdef _MSVC_LANG +#define CXX_STD_VER _MSVC_LANG +#else +#define CXX_STD_VER __cplusplus +#endif + +#if CXX_STD_VER >= 202002L +#include +namespace sqlpp +{ + template + using span = std::span; +} // namespace sqlpp + +#else // incomplete backport of std::span namespace sqlpp { - template - struct result_field_t> - : public result_field_base> + template + class span { - template - void _bind(Target& target, size_t index) + const T* _data = nullptr; + size_t _size = 0u; + public: + constexpr span() = default; + constexpr span(const T* data, size_t size) : _data(data), _size(size) { - target._bind_integral_result(index, &this->_value, &this->_is_null); } - template - void _post_bind(Target& target, size_t index) + const char* data() const { - target._post_bind_integral_result(index, &this->_value, &this->_is_null); + return _data; } + + size_t size() const + { + return _size; + } + + const T& operator[](size_t i) const + { + return *(_data + i); + } + }; + } // namespace sqlpp + +#endif diff --git a/include/sqlpp11/compat/string_view.h b/include/sqlpp11/compat/string_view.h new file mode 100644 index 00000000..b1863979 --- /dev/null +++ b/include/sqlpp11/compat/string_view.h @@ -0,0 +1,106 @@ +#pragma once + +/* + * Copyright (c) 2024, 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. + */ + +#ifdef _MSVC_LANG +#define CXX_STD_VER _MSVC_LANG +#else +#define CXX_STD_VER __cplusplus +#endif + +#if CXX_STD_VER >= 201703L +#include +namespace sqlpp +{ + using string_view = std::string_view; +} // namespace sqlpp + +#else // incomplete backport of std::string_view + +#include +#include +#include + +namespace sqlpp +{ + class string_view + { + const char* _data = nullptr; + size_t _size = 0u; + public: + constexpr string_view() = default; + string_view(const std::string& source) : _data(source.data()), _size(source.size()) + { + } + + constexpr string_view(const char* data, size_t size) : _data(data), _size(size) + { + } + + string_view(const char* data) : _data(data), _size(std::char_traits::length(data)) + { + } + + const char* data() const + { + return _data; + } + + size_t size() const + { + return _size; + } + + operator std::string() const + { + return std::string(_data, _size); + } + }; + + inline bool operator==(const string_view& left, const string_view& right) + { + if (left.size() != right.size()) + return false; + return std::char_traits::compare(left.data(), right.data(), left.size()) == 0; + } + + inline bool operator!=(const string_view& left, const string_view& right) + { + if (left.size() != right.size()) + return true; + return std::char_traits::compare(left.data(), right.data(), left.size()) != 0; + } + + inline std::ostream& operator<<(std::ostream& os, const string_view& sv) + { + return os << std::string(sv); + } + + +} // namespace sqlpp + +#endif diff --git a/include/sqlpp11/data_types/blob.h b/include/sqlpp11/data_types/blob.h index 3998491f..33f8ba90 100644 --- a/include/sqlpp11/data_types/blob.h +++ b/include/sqlpp11/data_types/blob.h @@ -32,7 +32,6 @@ #include #include #include -#include // blob specific functions #include diff --git a/include/sqlpp11/data_types/blob/data_type.h b/include/sqlpp11/data_types/blob/data_type.h index 04d3ab98..4a6e98ad 100644 --- a/include/sqlpp11/data_types/blob/data_type.h +++ b/include/sqlpp11/data_types/blob/data_type.h @@ -30,6 +30,7 @@ #include #include +#include #include namespace sqlpp @@ -38,6 +39,7 @@ namespace sqlpp { using _traits = make_traits; using _cpp_value_type = std::vector; + using _result_type = sqlpp::span; template using _is_valid_operand = ::sqlpp::logic::any_t::value, is_text_t::value>; diff --git a/include/sqlpp11/data_types/blob/result_field.h b/include/sqlpp11/data_types/blob/result_field.h deleted file mode 100644 index 9e911a99..00000000 --- a/include/sqlpp11/data_types/blob/result_field.h +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -/* - * Copyright (c) 2013-2017, 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 - -namespace sqlpp -{ - template - struct result_field_t> - : public result_field_base> - { - const uint8_t* blob{nullptr}; // Non-owning - size_t len{}; - - template - void _bind(Target& target, size_t index) - { - target._bind_blob_result(index, &blob, &len); - if (blob) - this->_value.assign(blob, blob + len); - else - this->_value.clear(); - this->_is_null = (blob == nullptr); - } - - template - void _post_bind(Target& target, size_t index) - { - target._post_bind_blob_result(index, &blob, &len); - if (blob) - this->_value.assign(blob, blob + len); - else - this->_value.clear(); - this->_is_null = (blob == nullptr); - } - }; - - template - inline std::ostream& operator<<( - std::ostream& os, const result_field_t>& e) - { - if (e.is_null()) - { - return os << "NULL"; - } - else - { - std::vector value = e.value(); - std::string value_str(value.begin(), value.end()); - return os << value_str; - } - } -} // namespace sqlpp diff --git a/include/sqlpp11/data_types/boolean.h b/include/sqlpp11/data_types/boolean.h index 794f2156..627aab6d 100644 --- a/include/sqlpp11/data_types/boolean.h +++ b/include/sqlpp11/data_types/boolean.h @@ -32,4 +32,3 @@ #include #include #include -#include diff --git a/include/sqlpp11/data_types/boolean/data_type.h b/include/sqlpp11/data_types/boolean/data_type.h index 5414c6f7..57370e8a 100644 --- a/include/sqlpp11/data_types/boolean/data_type.h +++ b/include/sqlpp11/data_types/boolean/data_type.h @@ -34,6 +34,7 @@ namespace sqlpp { using _traits = make_traits; using _cpp_value_type = bool; + using _result_type = bool; template using _is_valid_operand = is_boolean_t; diff --git a/include/sqlpp11/data_types/boolean/result_field.h b/include/sqlpp11/data_types/boolean/result_field.h deleted file mode 100644 index 05178101..00000000 --- a/include/sqlpp11/data_types/boolean/result_field.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -/* - * 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 - -namespace sqlpp -{ - template - struct result_field_t> - : public result_field_base, signed char> - { - template - void _bind(Target& target, size_t index) - { - target._bind_boolean_result(index, &this->_value, &this->_is_null); - } - - template - void _post_bind(Target& target, size_t index) - { - target._post_bind_boolean_result(index, &this->_value, &this->_is_null); - } - }; -} // namespace sqlpp diff --git a/include/sqlpp11/data_types/day_point.h b/include/sqlpp11/data_types/day_point.h index 00aa96d5..bad4fa51 100644 --- a/include/sqlpp11/data_types/day_point.h +++ b/include/sqlpp11/data_types/day_point.h @@ -32,4 +32,3 @@ #include #include #include -#include diff --git a/include/sqlpp11/data_types/day_point/data_type.h b/include/sqlpp11/data_types/day_point/data_type.h index f342f395..1a670a56 100644 --- a/include/sqlpp11/data_types/day_point/data_type.h +++ b/include/sqlpp11/data_types/day_point/data_type.h @@ -35,6 +35,7 @@ namespace sqlpp { using _traits = make_traits; using _cpp_value_type = ::sqlpp::chrono::day_point; + using _result_type = ::sqlpp::chrono::day_point; template using _is_valid_operand = is_day_or_time_point_t; diff --git a/include/sqlpp11/data_types/day_point/result_field.h b/include/sqlpp11/data_types/day_point/result_field.h deleted file mode 100644 index d5e91258..00000000 --- a/include/sqlpp11/data_types/day_point/result_field.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -/* - * Copyright (c) 2015-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 - -namespace sqlpp -{ - template - struct result_field_t> - : public result_field_base> - { - template - void _bind(Target& target, size_t index) - { - target._bind_date_result(index, &this->_value, &this->_is_null); - } - - template - void _post_bind(Target& target, size_t index) - { - target._post_bind_date_result(index, &this->_value, &this->_is_null); - } - }; - - template - inline std::ostream& operator<<( - std::ostream& os, const result_field_t>& e) - { - if (e.is_null()) - { - os << "NULL"; - } - else - { - const auto ymd = ::date::year_month_day{e.value()}; - os << ymd; - } - return os; - } -} // namespace sqlpp diff --git a/include/sqlpp11/data_types/floating_point.h b/include/sqlpp11/data_types/floating_point.h index b01ff577..cd872490 100644 --- a/include/sqlpp11/data_types/floating_point.h +++ b/include/sqlpp11/data_types/floating_point.h @@ -32,4 +32,3 @@ #include #include #include -#include diff --git a/include/sqlpp11/data_types/floating_point/data_type.h b/include/sqlpp11/data_types/floating_point/data_type.h index 7ad5d23d..c8e7fb98 100644 --- a/include/sqlpp11/data_types/floating_point/data_type.h +++ b/include/sqlpp11/data_types/floating_point/data_type.h @@ -34,6 +34,7 @@ namespace sqlpp { using _traits = make_traits; using _cpp_value_type = double; + using _result_type = double; template using _is_valid_operand = is_numeric_t; diff --git a/include/sqlpp11/data_types/floating_point/result_field.h b/include/sqlpp11/data_types/floating_point/result_field.h deleted file mode 100644 index 3ca41a5c..00000000 --- a/include/sqlpp11/data_types/floating_point/result_field.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -/* - * 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 - -namespace sqlpp -{ - template - struct result_field_t> - : public result_field_base> - { - template - void _bind(Target& target, size_t index) - { - target._bind_floating_point_result(index, &this->_value, &this->_is_null); - } - - template - void _post_bind(Target& target, size_t index) - { - target._post_bind_floating_point_result(index, &this->_value, &this->_is_null); - } - }; -} // namespace sqlpp diff --git a/include/sqlpp11/data_types/integral.h b/include/sqlpp11/data_types/integral.h index b6117ee9..0f8f1cad 100644 --- a/include/sqlpp11/data_types/integral.h +++ b/include/sqlpp11/data_types/integral.h @@ -32,4 +32,3 @@ #include #include #include -#include diff --git a/include/sqlpp11/data_types/integral/data_type.h b/include/sqlpp11/data_types/integral/data_type.h index 85198234..b2f2b38d 100644 --- a/include/sqlpp11/data_types/integral/data_type.h +++ b/include/sqlpp11/data_types/integral/data_type.h @@ -34,6 +34,7 @@ namespace sqlpp { using _traits = make_traits; using _cpp_value_type = int64_t; + using _result_type = int64_t; template using _is_valid_operand = is_numeric_t; diff --git a/include/sqlpp11/data_types/no_value.h b/include/sqlpp11/data_types/no_value.h index 17334540..be2e9716 100644 --- a/include/sqlpp11/data_types/no_value.h +++ b/include/sqlpp11/data_types/no_value.h @@ -32,4 +32,3 @@ #include #include #include -#include diff --git a/include/sqlpp11/data_types/no_value/result_field.h b/include/sqlpp11/data_types/no_value/result_field.h deleted file mode 100644 index f0783d83..00000000 --- a/include/sqlpp11/data_types/no_value/result_field.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -/* - * 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 - -namespace sqlpp -{ - template - struct result_field_t> - { - template - void _bind(Target& /*unused*/, size_t /*unused*/) - { - } - - template - void _post_bind(Target& /*unused*/, size_t /*unused*/) - { - } - - void _validate() const - { - } - - void _invalidate() const - { - } - - constexpr bool is_null() const - { - return true; - } - }; - - template - inline std::ostream& operator<<( - std::ostream& os, - const result_field_t>& /*unused*/) - { - os << "NULL"; - return os; - } -} // namespace sqlpp diff --git a/include/sqlpp11/data_types/text.h b/include/sqlpp11/data_types/text.h index 5824ac65..e236ddd2 100644 --- a/include/sqlpp11/data_types/text.h +++ b/include/sqlpp11/data_types/text.h @@ -32,7 +32,6 @@ #include #include #include -#include // text specific functions #include diff --git a/include/sqlpp11/data_types/text/data_type.h b/include/sqlpp11/data_types/text/data_type.h index 6504b23c..99c63d4e 100644 --- a/include/sqlpp11/data_types/text/data_type.h +++ b/include/sqlpp11/data_types/text/data_type.h @@ -28,12 +28,17 @@ #include +#include + +#include + namespace sqlpp { struct text { using _traits = make_traits; using _cpp_value_type = std::string; + using _result_type = sqlpp::string_view; template using _is_valid_operand = is_text_t; diff --git a/include/sqlpp11/data_types/text/operand.h b/include/sqlpp11/data_types/text/operand.h index 691307d0..cee99120 100644 --- a/include/sqlpp11/data_types/text/operand.h +++ b/include/sqlpp11/data_types/text/operand.h @@ -28,9 +28,7 @@ #include #include -#if __cplusplus >= 201703L -#include -#endif +#include #include #include @@ -51,16 +49,14 @@ namespace sqlpp text_operand(_value_t t) : _t(std::move(t)) { } -#if __cplusplus >= 201703L - // allow construction from an std::string_view - text_operand(std::string_view t) : _t(t) + // allow construction from an sqlpp::string_view + text_operand(sqlpp::string_view t) : _t(t) { } // additional const char* overload, required to disambiguate text_operand(const char* t) : _t(t) { } -#endif text_operand(const text_operand&) = default; text_operand(text_operand&&) = default; diff --git a/include/sqlpp11/data_types/text/parameter_value.h b/include/sqlpp11/data_types/text/parameter_value.h index 52fdeeaa..8a1aeab1 100644 --- a/include/sqlpp11/data_types/text/parameter_value.h +++ b/include/sqlpp11/data_types/text/parameter_value.h @@ -32,9 +32,7 @@ #include #include -#if __cplusplus >= 201703L -#include -#endif +#include namespace sqlpp { @@ -51,8 +49,7 @@ namespace sqlpp target._bind_text_parameter(index, &_value, _is_null); } -#if __cplusplus >= 201703L - parameter_value_base& operator=(const std::string_view& val) + parameter_value_base& operator=(const sqlpp::string_view& val) { _value = val; _is_null = false; @@ -65,6 +62,5 @@ namespace sqlpp _is_null = false; return *this; } -#endif }; } // namespace sqlpp diff --git a/include/sqlpp11/data_types/text/result_field.h b/include/sqlpp11/data_types/text/result_field.h deleted file mode 100644 index e97c9be5..00000000 --- a/include/sqlpp11/data_types/text/result_field.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -/* - * 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 - -namespace sqlpp -{ - template - struct result_field_t> - : public result_field_base> - { - const char* text{nullptr}; // Non-owning - size_t len{}; - - template - void _bind(Target& target, size_t index) - { - target._bind_text_result(index, &text, &len); - if (text) - { - this->_value.assign(text, len); - } - else - { - this->_value.assign(""); - } - this->_is_null = (text == nullptr); - } - - template - void _post_bind(Target& target, size_t index) - { - target._post_bind_text_result(index, &text, &len); - if (text) - { - this->_value.assign(text, len); - } - else - { - this->_value.assign(""); - } - this->_is_null = (text == nullptr); - } - }; - - template - inline std::ostream& operator<<( - std::ostream& os, const result_field_t>& e) - { - if (e.is_null()) - { - return os << "NULL"; - } - - return os << e.value(); - } -} // namespace sqlpp diff --git a/include/sqlpp11/data_types/text/wrap_operand.h b/include/sqlpp11/data_types/text/wrap_operand.h index 00ea3bc9..8d1e4ef5 100644 --- a/include/sqlpp11/data_types/text/wrap_operand.h +++ b/include/sqlpp11/data_types/text/wrap_operand.h @@ -27,21 +27,14 @@ */ #include -#if __cplusplus >= 201703L -#include -#endif +#include #include #include namespace sqlpp { struct text_operand; - -#if __cplusplus >= 201703L - using checked_type = std::string_view; -#else - using checked_type = std::string; -#endif + using checked_type = sqlpp::string_view; template struct wrap_operand< diff --git a/include/sqlpp11/data_types/time_of_day.h b/include/sqlpp11/data_types/time_of_day.h index d979197d..a4d46f56 100644 --- a/include/sqlpp11/data_types/time_of_day.h +++ b/include/sqlpp11/data_types/time_of_day.h @@ -32,4 +32,3 @@ #include #include #include -#include diff --git a/include/sqlpp11/data_types/time_of_day/data_type.h b/include/sqlpp11/data_types/time_of_day/data_type.h index c132b8a8..1b4c8eaa 100644 --- a/include/sqlpp11/data_types/time_of_day/data_type.h +++ b/include/sqlpp11/data_types/time_of_day/data_type.h @@ -35,6 +35,7 @@ namespace sqlpp { using _traits = make_traits; using _cpp_value_type = std::chrono::microseconds; + using _result_type = std::chrono::microseconds; template using _is_valid_operand = is_time_of_day_t; diff --git a/include/sqlpp11/data_types/time_of_day/result_field.h b/include/sqlpp11/data_types/time_of_day/result_field.h deleted file mode 100644 index d3ca4e51..00000000 --- a/include/sqlpp11/data_types/time_of_day/result_field.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -/* - * Copyright (c) 2015-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 - -namespace sqlpp -{ - template - struct result_field_t> - : public result_field_base> - { - template - void _bind(Target& target, size_t i) - { - target._bind_time_of_day_result(i, &this->_value, &this->_is_null); - } - - template - void _post_bind(Target& target, size_t i) - { - target._post_bind_time_of_day_result(i, &this->_value, &this->_is_null); - } - }; - - template - inline std::ostream& operator<<( - std::ostream& os, const result_field_t>& e) - { - if (e.is_null()) - { - os << "NULL"; - } - else - { - os << ::date::make_time(e.value()); - } - return os; - } -} // namespace sqlpp diff --git a/include/sqlpp11/data_types/time_point.h b/include/sqlpp11/data_types/time_point.h index 64e5199e..7bb8b3e3 100644 --- a/include/sqlpp11/data_types/time_point.h +++ b/include/sqlpp11/data_types/time_point.h @@ -32,4 +32,3 @@ #include #include #include -#include diff --git a/include/sqlpp11/data_types/time_point/data_type.h b/include/sqlpp11/data_types/time_point/data_type.h index 6c54a68e..f8187391 100644 --- a/include/sqlpp11/data_types/time_point/data_type.h +++ b/include/sqlpp11/data_types/time_point/data_type.h @@ -35,6 +35,7 @@ namespace sqlpp { using _traits = make_traits; using _cpp_value_type = ::sqlpp::chrono::microsecond_point; + using _result_type = ::sqlpp::chrono::microsecond_point; template using _is_valid_operand = is_day_or_time_point_t; diff --git a/include/sqlpp11/data_types/time_point/result_field.h b/include/sqlpp11/data_types/time_point/result_field.h deleted file mode 100644 index 238ccfb8..00000000 --- a/include/sqlpp11/data_types/time_point/result_field.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -/* - * Copyright (c) 2015-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 - -namespace sqlpp -{ - template - struct result_field_t> - : public result_field_base> - { - template - void _bind(Target& target, size_t i) - { - target._bind_date_time_result(i, &this->_value, &this->_is_null); - } - - template - void _post_bind(Target& target, size_t i) - { - target._post_bind_date_time_result(i, &this->_value, &this->_is_null); - } - }; - - template - inline std::ostream& operator<<( - std::ostream& os, const result_field_t>& e) - { - if (e.is_null()) - { - os << "NULL"; - } - else - { - const auto dp = ::sqlpp::chrono::floor<::date::days>(e.value()); - const auto time = ::date::make_time(e.value() - dp); - const auto ymd = ::date::year_month_day{dp}; - os << ymd << 'T' << time; - } - return os; - } -} // namespace sqlpp diff --git a/include/sqlpp11/data_types/unsigned_integral.h b/include/sqlpp11/data_types/unsigned_integral.h index c0868610..38f00402 100644 --- a/include/sqlpp11/data_types/unsigned_integral.h +++ b/include/sqlpp11/data_types/unsigned_integral.h @@ -32,4 +32,3 @@ #include #include #include -#include diff --git a/include/sqlpp11/data_types/unsigned_integral/data_type.h b/include/sqlpp11/data_types/unsigned_integral/data_type.h index 14527c54..5820a78e 100644 --- a/include/sqlpp11/data_types/unsigned_integral/data_type.h +++ b/include/sqlpp11/data_types/unsigned_integral/data_type.h @@ -34,6 +34,7 @@ namespace sqlpp { using _traits = make_traits; using _cpp_value_type = uint64_t; + using _result_type = uint64_t; template using _is_valid_operand = is_numeric_t; diff --git a/include/sqlpp11/data_types/unsigned_integral/result_field.h b/include/sqlpp11/data_types/unsigned_integral/result_field.h deleted file mode 100644 index 786bc941..00000000 --- a/include/sqlpp11/data_types/unsigned_integral/result_field.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -/* - * Copyright (c) 2013-2016, Roland Bock, Aaron Bishop - * 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 - -namespace sqlpp -{ - template - struct result_field_t> - : public result_field_base> - { - template - void _bind(Target& target, size_t index) - { - target._bind_unsigned_integral_result(index, &this->_value, &this->_is_null); - } - - template - void _post_bind(Target& target, size_t index) - { - target._post_bind_unsigned_integral_result(index, &this->_value, &this->_is_null); - } - }; -} // namespace sqlpp diff --git a/include/sqlpp11/eval.h b/include/sqlpp11/eval.h index cdf1f61c..7c548451 100644 --- a/include/sqlpp11/eval.h +++ b/include/sqlpp11/eval.h @@ -44,7 +44,7 @@ namespace sqlpp using _name_type = alias::a_t::_alias_t; using _value_type = value_type_of; using _field_spec = field_spec_t<_name_type, _value_type, true>; - using type = result_field_t; + using type = typename _field_spec::cpp_type; }; template +#include + +#include + namespace sqlpp { template @@ -39,6 +43,10 @@ namespace sqlpp using _nodes = detail::type_vector<>; using _alias_t = NameType; + + using cpp_type = typename std::conditional, + typename ValueType::_result_type>::type; }; template diff --git a/include/sqlpp11/mysql/bind_result.h b/include/sqlpp11/mysql/bind_result.h index ad859fcf..3b3d95d7 100644 --- a/include/sqlpp11/mysql/bind_result.h +++ b/include/sqlpp11/mysql/bind_result.h @@ -28,6 +28,9 @@ #include #include +#include +#include +#include #include #include @@ -45,6 +48,7 @@ namespace sqlpp { std::shared_ptr _handle; void* _result_row_address{nullptr}; + bool _require_bind = true; public: bind_result_t() = default; @@ -79,17 +83,23 @@ namespace sqlpp if (&result_row != _result_row_address) { - result_row._bind(*this); // sets row data to mysql bind data - bind_impl(); // binds mysql statement to data + result_row._bind_fields(*this); // sets row data to mysql bind data _result_row_address = &result_row; } + + if (_require_bind) + { + bind_impl(); // binds mysql statement to data + _require_bind = false; + } + if (next_impl()) { if (not result_row) { result_row._validate(); } - result_row._post_bind(*this); // translates bind_data to row data where required + result_row._read_fields(*this); // translates bind_data to row data where required } else { @@ -103,270 +113,289 @@ namespace sqlpp return !_handle or !*_handle; } - void _bind_boolean_result(size_t index, signed char* value, bool* is_null) + void bind_field(size_t index, bool& /*value*/) { if (_handle->debug) - std::cerr << "MySQL debug: binding boolean result " << static_cast(value) << " at index: " << index + std::cerr << "MySQL debug: binding boolean result at index: " << index << std::endl; - detail::result_meta_data_t& meta_data{_handle->result_param_meta_data[index]}; - meta_data.index = index; - meta_data.len = nullptr; - meta_data.is_null = is_null; + auto& buffer{_handle->result_buffers[index]}; + new (&buffer._bool) bool{}; MYSQL_BIND& param{_handle->result_params[index]}; param.buffer_type = MYSQL_TYPE_TINY; - param.buffer = value; - param.buffer_length = sizeof(*value); - param.length = &meta_data.bound_len; - param.is_null = &meta_data.bound_is_null; + param.buffer = &buffer._bool; + param.buffer_length = sizeof(buffer._bool); + param.length = &buffer.length; + param.is_null = &buffer.is_null; param.is_unsigned = false; - param.error = &meta_data.bound_error; + param.error = &buffer.error; } - void _bind_integral_result(size_t index, int64_t* value, bool* is_null) + void bind_field(size_t index, int64_t& /*value*/) { if (_handle->debug) - std::cerr << "MySQL debug: binding integral result " << static_cast(value) << " at index: " << index + std::cerr << "MySQL debug: binding integral result at index: " << index << std::endl; - detail::result_meta_data_t& meta_data{_handle->result_param_meta_data[index]}; - meta_data.index = index; - meta_data.len = nullptr; - meta_data.is_null = is_null; + auto& buffer{_handle->result_buffers[index]}; + new (&buffer._int64) int64_t{}; MYSQL_BIND& param{_handle->result_params[index]}; param.buffer_type = MYSQL_TYPE_LONGLONG; - param.buffer = value; - param.buffer_length = sizeof(*value); - param.length = &meta_data.bound_len; - param.is_null = &meta_data.bound_is_null; + param.buffer = &buffer._int64; + param.buffer_length = sizeof(buffer._int64); + param.length = &buffer.length; + param.is_null = &buffer.is_null; param.is_unsigned = false; - param.error = &meta_data.bound_error; + param.error = &buffer.error; } - void _bind_unsigned_integral_result(size_t index, uint64_t* value, bool* is_null) + void bind_field(size_t index, uint64_t& /*value*/) { if (_handle->debug) - std::cerr << "MySQL debug: binding unsigned integral result " << static_cast(value) - << " at index: " << index << std::endl; + std::cerr << "MySQL debug: binding unsigned integral result at index: " << index << std::endl; - detail::result_meta_data_t& meta_data{_handle->result_param_meta_data[index]}; - meta_data.index = index; - meta_data.len = nullptr; - meta_data.is_null = is_null; + auto& buffer{_handle->result_buffers[index]}; + new (&buffer._uint64) uint64_t{}; MYSQL_BIND& param{_handle->result_params[index]}; param.buffer_type = MYSQL_TYPE_LONGLONG; - param.buffer = value; - param.buffer_length = sizeof(*value); - param.length = &meta_data.bound_len; - param.is_null = &meta_data.bound_is_null; + param.buffer = &buffer._uint64; + param.buffer_length = sizeof(buffer._uint64); + param.length = &buffer.length; + param.is_null = &buffer.is_null; param.is_unsigned = true; - param.error = &meta_data.bound_error; + param.error = &buffer.error; } - void _bind_floating_point_result(size_t index, double* value, bool* is_null) + void bind_field(size_t index, double& /*value*/) { if (_handle->debug) - std::cerr << "MySQL debug: binding floating point result " << static_cast(value) - << " at index: " << index << std::endl; + std::cerr << "MySQL debug: binding floating point result at index: " << index << std::endl; - detail::result_meta_data_t& meta_data{_handle->result_param_meta_data[index]}; - meta_data.index = index; - meta_data.len = nullptr; - meta_data.is_null = is_null; + auto& buffer{_handle->result_buffers[index]}; + new (&buffer._double) double{}; MYSQL_BIND& param{_handle->result_params[index]}; param.buffer_type = MYSQL_TYPE_DOUBLE; - param.buffer = value; - param.buffer_length = sizeof(*value); - param.length = &meta_data.bound_len; - param.is_null = &meta_data.bound_is_null; + param.buffer = &buffer._double; + param.buffer_length = sizeof(buffer._double); + param.length = &buffer.length; + param.is_null = &buffer.is_null; param.is_unsigned = false; - param.error = &meta_data.bound_error; + param.error = &buffer.error; } - void _bind_text_result(size_t index, const char** value, size_t* len) + void bind_field(size_t index, sqlpp::string_view& /*value*/) { if (_handle->debug) - std::cerr << "MySQL debug: binding text result " << static_cast(*value) << " at index: " << index + std::cerr << "MySQL debug: binding text result at index: " << index << std::endl; - detail::result_meta_data_t& meta_data{_handle->result_param_meta_data[index]}; - meta_data.index = index; - meta_data.len = len; - meta_data.is_null = nullptr; - meta_data.text_buffer = value; - if (meta_data.bound_text_buffer.empty()) - meta_data.bound_text_buffer.resize(8); + auto& buffer{_handle->result_buffers[index]}; MYSQL_BIND& param{_handle->result_params[index]}; param.buffer_type = MYSQL_TYPE_STRING; - param.buffer = meta_data.bound_text_buffer.data(); - param.buffer_length = meta_data.bound_text_buffer.size(); - param.length = &meta_data.bound_len; - param.is_null = &meta_data.bound_is_null; + param.buffer = buffer.var_buffer.data(); + param.buffer_length = buffer.var_buffer.size(); + param.length = &buffer.length; + param.is_null = &buffer.is_null; param.is_unsigned = false; - param.error = &meta_data.bound_error; + param.error = &buffer.error; } - void _bind_blob_result(size_t index, const char** value, size_t* len) + + void bind_field(size_t index, sqlpp::span& /*value*/) { if (_handle->debug) - std::cerr << "MySQL debug: binding text result " << static_cast(*value) << " at index: " << index + std::cerr << "MySQL debug: binding blob result at index: " << index << std::endl; - detail::result_meta_data_t& meta_data{_handle->result_param_meta_data[index]}; - meta_data.index = index; - meta_data.len = len; - meta_data.is_null = nullptr; - meta_data.text_buffer = value; - if (meta_data.bound_text_buffer.empty()) - meta_data.bound_text_buffer.resize(8); + auto& buffer{_handle->result_buffers[index]}; MYSQL_BIND& param{_handle->result_params[index]}; param.buffer_type = MYSQL_TYPE_BLOB; - param.buffer = meta_data.bound_text_buffer.data(); - param.buffer_length = meta_data.bound_text_buffer.size(); - param.length = &meta_data.bound_len; - param.is_null = &meta_data.bound_is_null; + param.buffer = buffer.var_buffer.data(); + param.buffer_length = buffer.var_buffer.size(); + param.length = &buffer.length; + param.is_null = &buffer.is_null; param.is_unsigned = false; - param.error = &meta_data.bound_error; + param.error = &buffer.error; } - void _bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null) + void bind_chrono_field(size_t index, enum_field_types buffer_type) { - if (_handle->debug) - std::cerr << "MySQL debug: binding date result " << static_cast(value) << " at index: " << index - << std::endl; - - detail::result_meta_data_t& meta_data{_handle->result_param_meta_data[index]}; - meta_data.index = index; - meta_data.len = nullptr; - meta_data.is_null = is_null; - meta_data.text_buffer = nullptr; - meta_data.bound_text_buffer.resize(sizeof(MYSQL_TIME)); + auto& buffer{_handle->result_buffers[index]}; + new (&buffer._mysql_time) MYSQL_TIME{}; MYSQL_BIND& param{_handle->result_params[index]}; - param.buffer_type = MYSQL_TYPE_DATE; - param.buffer = meta_data.bound_text_buffer.data(); - param.buffer_length = meta_data.bound_text_buffer.size(); - param.length = &meta_data.bound_len; - param.is_null = &meta_data.bound_is_null; + param.buffer_type = buffer_type; + param.buffer = &buffer._mysql_time; + param.buffer_length = sizeof(buffer._mysql_time); + param.length = &buffer.length; + param.is_null = &buffer.is_null; param.is_unsigned = false; - param.error = &meta_data.bound_error; + param.error = &buffer.error; } - void _bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null) + void bind_field(size_t index, ::sqlpp::chrono::day_point& /*value*/) { if (_handle->debug) - std::cerr << "MySQL debug: binding date time result " << static_cast(value) << " at index: " << index + std::cerr << "MySQL debug: binding date result at index: " << index << std::endl; - detail::result_meta_data_t& meta_data{_handle->result_param_meta_data[index]}; - meta_data.index = index; - meta_data.len = nullptr; - meta_data.is_null = is_null; - meta_data.text_buffer = nullptr; - meta_data.bound_text_buffer.resize(sizeof(MYSQL_TIME)); - - MYSQL_BIND& param{_handle->result_params[index]}; - param.buffer_type = MYSQL_TYPE_DATETIME; - param.buffer = meta_data.bound_text_buffer.data(); - param.buffer_length = meta_data.bound_text_buffer.size(); - param.length = &meta_data.bound_len; - param.is_null = &meta_data.bound_is_null; - param.is_unsigned = false; - param.error = &meta_data.bound_error; + bind_chrono_field(index, MYSQL_TYPE_DATE); } - void _bind_time_of_day_result(size_t index, ::std::chrono::microseconds* value, bool* is_null) + void bind_field(size_t index, ::sqlpp::chrono::microsecond_point& /*value*/) { if (_handle->debug) - std::cerr << "MySQL debug: binding time of day result " << static_cast(value) << " at index: " << index + std::cerr << "MySQL debug: binding date time result at index: " << index << std::endl; - detail::result_meta_data_t& meta_data{_handle->result_param_meta_data[index]}; - meta_data.index = index; - meta_data.len = nullptr; - meta_data.is_null = is_null; - meta_data.text_buffer = nullptr; - meta_data.bound_text_buffer.resize(sizeof(MYSQL_TIME)); - - MYSQL_BIND& param{_handle->result_params[index]}; - param.buffer_type = MYSQL_TYPE_TIME; - param.buffer = meta_data.bound_text_buffer.data(); - param.buffer_length = meta_data.bound_text_buffer.size(); - param.length = &meta_data.bound_len; - param.is_null = &meta_data.bound_is_null; - param.is_unsigned = false; - param.error = &meta_data.bound_error; + bind_chrono_field(index, MYSQL_TYPE_DATETIME); } - void _post_bind_boolean_result(size_t /* index */, signed char* /* value */, bool* /* is_null */) - { - } - void _post_bind_floating_point_result(size_t /* index */, double* /* value */, bool* /* is_null */) - { - } - void _post_bind_integral_result(size_t /* index */, int64_t* /* value */, bool* /* is_null */) - { - } - void _post_bind_unsigned_integral_result(size_t /* index */, uint64_t* /* value */, bool* /* is_null */) - { - } - void _post_bind_text_result(size_t /* index */, const char** /* text */, size_t* /* len */) - { - } - void _post_bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null) + void bind_field(size_t index, ::std::chrono::microseconds& /*value*/) { if (_handle->debug) - std::cerr << "MySQL debug: post binding date result " << static_cast(value) << " at index: " << index + std::cerr << "MySQL debug: binding time of day result at index: " << index << std::endl; - if (not *is_null) + bind_chrono_field(index, MYSQL_TYPE_TIME); + } + + template + void bind_field(size_t index, sqlpp::optional& value) + { + value = T{}; + bind_field(index, *value); + } + + void read_field(size_t index, bool& value) + { + if (_handle->debug) + std::cerr << "MySQL debug: reading bool result at index: " << index + << std::endl; + value = _handle->result_buffers[index]._bool; + } + + void read_field(size_t index, int64_t& value) + { + if (_handle->debug) + std::cerr << "MySQL debug: reading integral result at index: " << index + << std::endl; + value = _handle->result_buffers[index]._int64; + } + + void read_field(size_t index, uint64_t& value) + { + if (_handle->debug) + std::cerr << "MySQL debug: reading unsigned integral result at index: " << index + << std::endl; + value = _handle->result_buffers[index]._uint64; + } + + void read_field(size_t index, double& value) + { + if (_handle->debug) + std::cerr << "MySQL debug: reading floating point result at index: " << index + << std::endl; + value = _handle->result_buffers[index]._double; + } + + void refetch_if_required(size_t index) + { + if (_handle->debug) + std::cerr << "MySQL debug: Checking result size at index: " << index + << std::endl; + auto& buffer = _handle->result_buffers[index]; + auto& params = _handle->result_params[index]; + if (*params.length > params.buffer_length) { - const auto& dt = - *reinterpret_cast(_handle->result_param_meta_data[index].bound_text_buffer.data()); - if (dt.year > std::numeric_limits::max()) - throw sqlpp::exception{"cannot read year from db: " + std::to_string(dt.year)}; - *is_null = false; - *value = ::date::year(static_cast(dt.year)) / ::date::month(dt.month) / ::date::day(dt.day); + if (_handle->debug) + std::cerr << "MySQL debug: increasing buffer at: " << index << " to " << *params.length << std::endl; + buffer.var_buffer.resize(*params.length); + params.buffer = buffer.var_buffer.data(); + params.buffer_length = buffer.var_buffer.size(); + const auto err = mysql_stmt_fetch_column(_handle->mysql_stmt, ¶ms, index, 0); + if (err) + throw sqlpp::exception{std::string{"MySQL: Fetch column after reallocate failed: "} + "error-code: " + + std::to_string(err) + ", stmt-error: " + mysql_stmt_error(_handle->mysql_stmt) + + ", stmt-errno: " + std::to_string(mysql_stmt_errno(_handle->mysql_stmt))}; + _require_bind = true; } } - void _post_bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null) + void read_field(size_t index, sqlpp::string_view& value) { if (_handle->debug) - std::cerr << "MySQL debug: post binding date time result " << static_cast(value) << " at index: " << index + std::cerr << "MySQL debug: reading text result at index: " << index << std::endl; - - if (not *is_null) - { - const auto& dt = - *reinterpret_cast(_handle->result_param_meta_data[index].bound_text_buffer.data()); - if (dt.year > std::numeric_limits::max()) - throw sqlpp::exception{"cannot read year from db: " + std::to_string(dt.year)}; - *is_null = false; - *value = ::sqlpp::chrono::day_point(::date::year(static_cast(dt.year)) / ::date::month(dt.month) / ::date::day(dt.day)) + - std::chrono::hours(dt.hour) + std::chrono::minutes(dt.minute) + std::chrono::seconds(dt.second) + - std::chrono::microseconds(dt.second_part); - } + refetch_if_required(index); + const auto& buffer = _handle->result_buffers[index]; + const auto& params = _handle->result_params[index]; + value = sqlpp::string_view(buffer.var_buffer.data(), *params.length); } - void _post_bind_time_of_day_result(size_t index, ::std::chrono::microseconds* value, bool* is_null) + void read_field(size_t index, sqlpp::span& value) { if (_handle->debug) - std::cerr << "MySQL debug: post binding date time result " << static_cast(value) << " at index: " << index + std::cerr << "MySQL debug: reading blob result at index: " << index + << std::endl; + refetch_if_required(index); + const auto& buffer = _handle->result_buffers[index]; + const auto& params = _handle->result_params[index]; + value = sqlpp::span(reinterpret_cast(buffer.var_buffer.data()), *params.length); + } + + void read_field(size_t index, ::sqlpp::chrono::day_point& value) + { + if (_handle->debug) + std::cerr << "MySQL debug: reading date result at index: " << index << std::endl; - if (not *is_null) + const auto& dt = _handle->result_buffers[index]._mysql_time; + if (dt.year > std::numeric_limits::max()) + throw sqlpp::exception{"cannot read year from db: " + std::to_string(dt.year)}; + value = ::date::year(static_cast(dt.year)) / ::date::month(dt.month) / ::date::day(dt.day); + } + + void read_field(size_t index, ::sqlpp::chrono::microsecond_point& value) + { + if (_handle->debug) + std::cerr << "MySQL debug: reading date time result at index: " << index << std::endl; + + const auto& dt = _handle->result_buffers[index]._mysql_time; + if (dt.year > std::numeric_limits::max()) + throw sqlpp::exception{"cannot read year from db: " + std::to_string(dt.year)}; + value = ::sqlpp::chrono::day_point(::date::year(static_cast(dt.year)) / ::date::month(dt.month) / + ::date::day(dt.day)) + + std::chrono::hours(dt.hour) + std::chrono::minutes(dt.minute) + std::chrono::seconds(dt.second) + + std::chrono::microseconds(dt.second_part); + } + + void read_field(size_t index, ::std::chrono::microseconds& value) + { + if (_handle->debug) + std::cerr << "MySQL debug: reading date time result at index: " << index << std::endl; + + const auto& dt = _handle->result_buffers[index]._mysql_time; + value = std::chrono::hours(dt.hour) + std::chrono::minutes(dt.minute) + std::chrono::seconds(dt.second) + + std::chrono::microseconds(dt.second_part); + } + + template + void read_field(size_t index, sqlpp::optional& value) + { + if (_handle->result_buffers[index].is_null) { - const auto& dt = - *reinterpret_cast(_handle->result_param_meta_data[index].bound_text_buffer.data()); - *value = std::chrono::hours(dt.hour) + std::chrono::minutes(dt.minute) + std::chrono::seconds(dt.second) + - std::chrono::microseconds(dt.second_part); + value.reset(); + return; } + if (!value) + value = T{}; + read_field(index, *value); } private: @@ -387,58 +416,12 @@ namespace sqlpp if (_handle->debug) std::cerr << "MySQL debug: Accessing next row of handle at " << _handle.get() << std::endl; - auto flag = mysql_stmt_fetch(_handle->mysql_stmt); + const auto flag = mysql_stmt_fetch(_handle->mysql_stmt); switch (flag) { case 0: case MYSQL_DATA_TRUNCATED: - { - bool need_to_rebind{false}; - for (auto& r : _handle->result_param_meta_data) - { - if (r.len) - { - if (r.bound_is_null) - { - *r.text_buffer = nullptr; - *r.len = 0; - } - else - { - if (r.bound_len > r.bound_text_buffer.size()) - { - if (_handle->debug) - std::cerr << "MySQL debug: Need to reallocate buffer " << static_cast(*r.text_buffer) - << " at index " << r.index << " for handle at " << _handle.get() << std::endl; - need_to_rebind = true; - r.bound_text_buffer.resize(r.bound_len); - MYSQL_BIND& param{_handle->result_params[r.index]}; - param.buffer = r.bound_text_buffer.data(); - param.buffer_length = r.bound_text_buffer.size(); - - auto err = - mysql_stmt_fetch_column(_handle->mysql_stmt, ¶m, static_cast(r.index), 0); - if (err) - throw sqlpp::exception{std::string{"MySQL: Fetch column after reallocate failed: "} + - "error-code: " + std::to_string(err) + - ", stmt-error: " + mysql_stmt_error(_handle->mysql_stmt) + - ", stmt-errno: " + std::to_string(mysql_stmt_errno(_handle->mysql_stmt))}; - } - *r.text_buffer = r.bound_text_buffer.data(); - if (_handle->debug) - std::cerr << "MySQL debug: New buffer " << static_cast(*r.text_buffer) << " at index " - << r.index << " for handle at " << _handle.get() << std::endl; - - *r.len = r.bound_len; - } - } - if (r.is_null) - *r.is_null = r.bound_is_null; - } - if (need_to_rebind) - bind_impl(); - } return true; case 1: throw sqlpp::exception{std::string{"MySQL: Could not fetch next result: "} + diff --git a/include/sqlpp11/mysql/char_result.h b/include/sqlpp11/mysql/char_result.h index a3cfc6f9..9d8f704d 100644 --- a/include/sqlpp11/mysql/char_result.h +++ b/include/sqlpp11/mysql/char_result.h @@ -36,6 +36,9 @@ #include #include #include +#include +#include +#include namespace sqlpp { @@ -88,7 +91,7 @@ namespace sqlpp { result_row._validate(); } - result_row._bind(*this); + result_row._read_fields(*this); } else { @@ -102,114 +105,102 @@ namespace sqlpp return !_handle or !*_handle; } - void _bind_boolean_result(size_t index, signed char* value, bool* is_null) + void read_field(size_t index, bool& value) { - *is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr); - *value = - (*is_null ? false : (_char_result_row.data[index][0] == 't' or _char_result_row.data[index][0] == '1')); + value = (_char_result_row.data[index][0] == 't' or _char_result_row.data[index][0] == '1'); } - void _bind_floating_point_result(size_t index, double* value, bool* is_null) + void read_field(size_t index, double& value) { - *is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr); - *value = (*is_null ? 0 : std::strtod(_char_result_row.data[index], nullptr)); + value = std::strtod(_char_result_row.data[index], nullptr); } - void _bind_integral_result(size_t index, int64_t* value, bool* is_null) + void read_field(size_t index, int64_t& value) { - *is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr); - *value = (*is_null ? 0 : std::strtoll(_char_result_row.data[index], nullptr, 10)); + value = std::strtoll(_char_result_row.data[index], nullptr, 10); } - void _bind_unsigned_integral_result(size_t index, uint64_t* value, bool* is_null) + void read_field(size_t index, uint64_t& value) { - *is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr); - *value = (*is_null ? 0 : std::strtoull(_char_result_row.data[index], nullptr, 10)); + value = std::strtoull(_char_result_row.data[index], nullptr, 10); } - void _bind_blob_result(size_t index, const uint8_t** value, size_t* len) + void read_field(size_t index, sqlpp::span& value) { - bool is_null{_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr}; - *value = (uint8_t*)(is_null ? nullptr : _char_result_row.data[index]); - *len = (is_null ? 0 : _char_result_row.len[index]); + value = sqlpp::span(reinterpret_cast(_char_result_row.data[index]), _char_result_row.len[index]); } - void _bind_text_result(size_t index, const char** value, size_t* len) + void read_field(size_t index, sqlpp::string_view& value) { - bool is_null{_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr}; - *value = (is_null ? nullptr : _char_result_row.data[index]); - *len = (is_null ? 0 : _char_result_row.len[index]); + value = sqlpp::string_view(_char_result_row.data[index], _char_result_row.len[index]); } - void _bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null) + void read_field(size_t index, ::sqlpp::chrono::day_point& value) { if (_handle->debug) std::cerr << "MySQL debug: parsing date result at index: " << index << std::endl; - *value = {}; - *is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr); - if (*is_null) - { - return; - } - const auto date_string = _char_result_row.data[index]; if (_handle->debug) std::cerr << "MySQL debug: date string: " << date_string << std::endl; - if (::sqlpp::detail::parse_date(*value, date_string) == false) + if (::sqlpp::detail::parse_date(value, date_string) == false) { if (_handle->debug) std::cerr << "MySQL debug: invalid date result: " << date_string << std::endl; } } - void _bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null) + void read_field(size_t index, ::sqlpp::chrono::microsecond_point& value) { if (_handle->debug) std::cerr << "MySQL debug: parsing date result at index: " << index << std::endl; - *value = {}; - *is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr); - if (*is_null) - { - return; - } - const auto date_time_string = _char_result_row.data[index]; if (_handle->debug) std::cerr << "MySQL debug: date_time string: " << date_time_string << std::endl; - if (::sqlpp::detail::parse_timestamp(*value, date_time_string) == false) + if (::sqlpp::detail::parse_timestamp(value, date_time_string) == false) { if (_handle->debug) std::cerr << "MySQL debug: invalid date_time result: " << date_time_string << std::endl; } } - void _bind_time_of_day_result(size_t index, ::std::chrono::microseconds* value, bool* is_null) + void read_field(size_t index, ::std::chrono::microseconds& value) { if (_handle->debug) std::cerr << "MySQL debug: parsing time of day result at index: " << index << std::endl; - *value = {}; - *is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr); - if (*is_null) - { - return; - } - const auto time_string = _char_result_row.data[index]; if (_handle->debug) std::cerr << "MySQL debug: time of day string: " << time_string << std::endl; - if (::sqlpp::detail::parse_time_of_day(*value, time_string) == false) + if (::sqlpp::detail::parse_time_of_day(value, time_string) == false) { if (_handle->debug) std::cerr << "MySQL debug: invalid time result: " << time_string << std::endl; } } + template + auto read_field(size_t index, sqlpp::optional& value) -> void + { + const bool is_null = _char_result_row.data[index] == nullptr; + if (is_null) + { + value.reset(); + } + else + { + if (not value) + { + value = T{}; + } + read_field(index, *value); + } + } + private: bool next_impl() { diff --git a/include/sqlpp11/mysql/detail/prepared_statement_handle.h b/include/sqlpp11/mysql/detail/prepared_statement_handle.h index cd424466..de4f51a6 100644 --- a/include/sqlpp11/mysql/detail/prepared_statement_handle.h +++ b/include/sqlpp11/mysql/detail/prepared_statement_handle.h @@ -36,16 +36,20 @@ namespace sqlpp { namespace detail { - struct result_meta_data_t + struct bind_result_buffer { - size_t index; - unsigned long bound_len; - my_bool bound_is_null; - my_bool bound_error; - std::vector bound_text_buffer; // also for blobs - const char** text_buffer; - size_t* len; - bool* is_null; + unsigned long length; + my_bool is_null; + my_bool error; + union // unnamed union injects members into scope + { + bool _bool; + int64_t _int64; + uint64_t _uint64; + double _double; + MYSQL_TIME _mysql_time; + }; + std::vector var_buffer; // text and blobs }; struct prepared_statement_handle_t @@ -72,7 +76,7 @@ namespace sqlpp std::vector stmt_date_time_param_buffer; std::vector stmt_param_is_null; // my_bool is bool after 8.0, and vector is bad std::vector result_params; - std::vector result_param_meta_data; + std::vector result_buffers; bool debug; prepared_statement_handle_t(MYSQL_STMT* stmt, size_t no_of_parameters, size_t no_of_columns, bool debug_) @@ -81,7 +85,7 @@ namespace sqlpp stmt_date_time_param_buffer(no_of_parameters, MYSQL_TIME{}), // ()-init for correct constructor stmt_param_is_null(no_of_parameters, false), // ()-init for correct constructor result_params(no_of_columns, MYSQL_BIND{}), // ()-init for correct constructor - result_param_meta_data(no_of_columns, result_meta_data_t{}), // ()-init for correct constructor + result_buffers(no_of_columns, bind_result_buffer{}), // ()-init for correct constructor debug{debug_} { } diff --git a/include/sqlpp11/postgresql/bind_result.h b/include/sqlpp11/postgresql/bind_result.h index 9dc7086e..a67b23c9 100644 --- a/include/sqlpp11/postgresql/bind_result.h +++ b/include/sqlpp11/postgresql/bind_result.h @@ -30,6 +30,9 @@ #include #include #include +#include +#include +#include #include #include @@ -49,10 +52,63 @@ namespace sqlpp namespace detail { struct statement_handle_t; - } + + inline unsigned char unhex(unsigned char c) + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return c - '0'; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + return c + 10 - 'a'; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + return c + 10 - 'A'; + } + throw sqlpp::exception{std::string{"Unexpected hex char: "} + static_cast(c)}; + } + + inline size_t hex_assign(std::vector& value, const uint8_t* blob, size_t len) + { + const auto result_size = len / 2 - 1; // unhex - leading chars + if (value.size() < result_size) + { + value.resize(result_size); // unhex - leading chars + } + size_t val_index = 0; + size_t blob_index = 2; + while (blob_index < len) + { + value[val_index] = static_cast(unhex(blob[blob_index]) << 4) + unhex(blob[blob_index + 1]); + ++val_index; + blob_index += 2; + } + return result_size; + } + } // namespace detail class bind_result_t { + // Need to buffer blobs (or switch to PQexecParams with binary results) + std::vector> _var_buffers; private: std::shared_ptr _handle; @@ -97,9 +153,9 @@ namespace sqlpp bind_result_t(const std::shared_ptr& handle) : _handle(handle) { + _var_buffers.resize(_handle->result.field_count()); if (this->_handle && this->_handle->debug()) { - // cerr std::cerr << "PostgreSQL debug: constructing bind result, using handle at: " << this->_handle.get() << std::endl; } @@ -131,7 +187,7 @@ namespace sqlpp { result_row._validate(); } - result_row._bind(*this); + result_row._read_fields(*this); } else { @@ -142,72 +198,60 @@ namespace sqlpp } } - void _bind_boolean_result(size_t _index, signed char* value, bool* is_null) + void read_field(size_t _index, bool& value) { - auto index = static_cast(_index); + const auto index = static_cast(_index); if (_handle->debug()) { - std::cerr << "PostgreSQL debug: binding boolean result at index: " << index << std::endl; + std::cerr << "PostgreSQL debug: reading boolean result at index: " << index << std::endl; } - *is_null = _handle->result.is_null(_handle->count, index); - *value = _handle->result.get_bool_value(_handle->count, index); + value = _handle->result.get_bool_value(_handle->count, index); } - void _bind_floating_point_result(size_t _index, double* value, bool* is_null) + void read_field(size_t _index, double& value) { - auto index = static_cast(_index); + const auto index = static_cast(_index); if (_handle->debug()) { - std::cerr << "PostgreSQL debug: binding floating_point result at index: " << index << std::endl; + std::cerr << "PostgreSQL debug: reading floating_point result at index: " << index << std::endl; } - *is_null = _handle->result.is_null(_handle->count, index); - *value = _handle->result.get_double_value(_handle->count, index); + value = _handle->result.get_double_value(_handle->count, index); } - void _bind_integral_result(size_t _index, int64_t* value, bool* is_null) + void read_field(size_t _index, int64_t& value) { - auto index = static_cast(_index); + const auto index = static_cast(_index); if (_handle->debug()) { - std::cerr << "PostgreSQL debug: binding integral result at index: " << index << std::endl; + std::cerr << "PostgreSQL debug: reading integral result at index: " << index << std::endl; } - *is_null = _handle->result.is_null(_handle->count, index); - *value = _handle->result.get_int64_value(_handle->count, index); + value = _handle->result.get_int64_value(_handle->count, index); } - void _bind_unsigned_integral_result(size_t _index, uint64_t* value, bool* is_null) + void read_field(size_t _index, uint64_t& value) { - auto index = static_cast(_index); + const auto index = static_cast(_index); if (_handle->debug()) { - std::cerr << "PostgreSQL debug: binding unsigned integral result at index: " << index << std::endl; + std::cerr << "PostgreSQL debug: reading unsigned integral result at index: " << index << std::endl; } - *is_null = _handle->result.is_null(_handle->count, index); - *value = _handle->result.get_uint64_value(_handle->count, index); + value = _handle->result.get_uint64_value(_handle->count, index); } - void _bind_text_result(size_t _index, const char** value, size_t* len) + void read_field(size_t _index, sqlpp::string_view& value) { - auto index = static_cast(_index); + const auto index = static_cast(_index); if (_handle->debug()) { - std::cerr << "PostgreSQL debug: binding text result at index: " << index << std::endl; + std::cerr << "PostgreSQL debug: reading text result at index: " << index << std::endl; } - if (_handle->result.is_null(_handle->count, index)) - { - *value = nullptr; - *len = 0; - } - else - { - *value = _handle->result.get_char_ptr_value(_handle->count, index); - *len = static_cast(_handle->result.length(_handle->count, index)); - } + value = sqlpp::string_view(_handle->result.get_char_ptr_value(_handle->count, index), + static_cast(_handle->result.length(_handle->count, index))); } // PostgreSQL will return one of those (using the default ISO client): @@ -218,20 +262,13 @@ namespace sqlpp // 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 ! - void _bind_date_result(size_t _index, ::sqlpp::chrono::day_point* value, bool* is_null) + void read_field(size_t _index, ::sqlpp::chrono::day_point& value) { - auto index = static_cast(_index); + const auto index = static_cast(_index); if (_handle->debug()) { - std::cerr << "PostgreSQL debug: binding date result at index: " << index << std::endl; - } - - *value = {}; - *is_null = _handle->result.is_null(_handle->count, index); - if (*is_null) - { - return; + std::cerr << "PostgreSQL debug: reading date result at index: " << index << std::endl; } const auto date_string = _handle->result.get_char_ptr_value(_handle->count, index); @@ -239,7 +276,7 @@ namespace sqlpp { std::cerr << "PostgreSQL debug: date string: " << date_string << std::endl; } - if (::sqlpp::detail::parse_date(*value, date_string) == false) + if (::sqlpp::detail::parse_date(value, date_string) == false) { if (_handle->debug()) { @@ -249,19 +286,12 @@ namespace sqlpp } // always returns UTC time for timestamp with time zone - void _bind_date_time_result(size_t _index, ::sqlpp::chrono::microsecond_point* value, bool* is_null) + void read_field(size_t _index, ::sqlpp::chrono::microsecond_point& value) { - auto index = static_cast(_index); + const auto index = static_cast(_index); if (_handle->debug()) { - std::cerr << "PostgreSQL debug: binding date_time result at index: " << index << std::endl; - } - - *value = {}; - *is_null = _handle->result.is_null(_handle->count, index); - if (*is_null) - { - return; + std::cerr << "PostgreSQL debug: reading date_time result at index: " << index << std::endl; } const auto date_string = _handle->result.get_char_ptr_value(_handle->count, index); @@ -269,7 +299,7 @@ namespace sqlpp { std::cerr << "PostgreSQL debug: got date_time string: " << date_string << std::endl; } - if (::sqlpp::detail::parse_timestamp(*value, date_string) == false) + if (::sqlpp::detail::parse_timestamp(value, date_string) == false) { if (_handle->debug()) { @@ -279,19 +309,12 @@ namespace sqlpp } // always returns UTC time for time with time zone - void _bind_time_of_day_result(size_t _index, ::std::chrono::microseconds* value, bool* is_null) + void read_field(size_t _index, ::std::chrono::microseconds& value) { - auto index = static_cast(_index); + const auto index = static_cast(_index); if (_handle->debug()) { - std::cerr << "PostgreSQL debug: binding time result at index: " << index << std::endl; - } - - *value = {}; - *is_null = _handle->result.is_null(_handle->count, index); - if (*is_null) - { - return; + std::cerr << "PostgreSQL debug: reading time result at index: " << index << std::endl; } const auto time_string = _handle->result.get_char_ptr_value(_handle->count, index); @@ -301,7 +324,7 @@ namespace sqlpp std::cerr << "PostgreSQL debug: got time string: " << time_string << std::endl; } - if (::sqlpp::detail::parse_time_of_day(*value, time_string) == false) + if (::sqlpp::detail::parse_time_of_day(value, time_string) == false) { if (_handle->debug()) { std::cerr << "PostgreSQL debug: got invalid time '" << time_string << "'" << std::endl; @@ -309,23 +332,39 @@ namespace sqlpp } } - void _bind_blob_result(size_t _index, const uint8_t** value, size_t* len) + void read_field(size_t _index, sqlpp::span& value) { - auto index = static_cast(_index); + const auto index = static_cast(_index); if (_handle->debug()) { - std::cerr << "PostgreSQL debug: binding blob result at index: " << index << std::endl; + std::cerr << "PostgreSQL debug: reading blob result at index: " << index << std::endl; } + // Need to decode the hex data. + // Using PQexecParams would allow to use binary data. + // That's certainly faster, but some effort to determine the correct column types. + const auto size = + detail::hex_assign(_var_buffers[_index], _handle->result.get_blob_value(_handle->count, index), + static_cast(_handle->result.length(_handle->count, index))); + + value = sqlpp::span(_var_buffers[_index].data(), size); + } + + template + auto read_field(size_t _index, sqlpp::optional& value) -> void + { + const auto index = static_cast(_index); if (_handle->result.is_null(_handle->count, index)) { - *value = nullptr; - *len = 0; + value.reset(); } else { - *value = _handle->result.get_blob_value(_handle->count, index); - *len = static_cast(_handle->result.length(_handle->count, index)); + if (not value) + { + value = T{}; + } + read_field(_index, *value); } } diff --git a/include/sqlpp11/postgresql/connection.h b/include/sqlpp11/postgresql/connection.h index 590cd1f5..80a1a84d 100644 --- a/include/sqlpp11/postgresql/connection.h +++ b/include/sqlpp11/postgresql/connection.h @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include diff --git a/include/sqlpp11/postgresql/detail/prepared_statement_handle.h b/include/sqlpp11/postgresql/detail/prepared_statement_handle.h index 271f0c79..568fee87 100644 --- a/include/sqlpp11/postgresql/detail/prepared_statement_handle.h +++ b/include/sqlpp11/postgresql/detail/prepared_statement_handle.h @@ -141,8 +141,10 @@ namespace sqlpp valid = false; count = 0; total_count = 0; - result = PQexecPrepared(connection.native_handle(), _name.data(), static_cast(size), values.data(), nullptr, nullptr, 0); - /// @todo validate result? is it really valid + result = PQexecPrepared(connection.native_handle(), /*stmtName*/ _name.data(), + /*nParams*/ static_cast(size), /*paramValues*/ values.data(), + /*paramLengths*/ nullptr, /*paramFormats*/ nullptr, /*resultFormat*/ 0); + /// @todo validate result? is it really valid valid = true; } @@ -171,7 +173,8 @@ namespace sqlpp void prepare(const std::string& stmt) { // Create the prepared statement - result = PQprepare(connection.native_handle(), _name.c_str(), stmt.c_str(), 0, nullptr); + result = PQprepare(connection.native_handle(), _name.c_str(), stmt.c_str(), + /*nParams*/ 0, /*paramTypes*/ nullptr); valid = true; } }; diff --git a/include/sqlpp11/postgresql/result.h b/include/sqlpp11/postgresql/result.h index 820c00ee..a00ce295 100644 --- a/include/sqlpp11/postgresql/result.h +++ b/include/sqlpp11/postgresql/result.h @@ -118,6 +118,7 @@ namespace sqlpp check_index(record, field); auto t = int64_t{}; const auto txt = std::string{get_pq_value(m_result, record, field)}; + std::cerr << "txt: " << txt << ", record: " << record << ", field: " << field << std::endl; if(txt != "") { t = std::stoll(txt); diff --git a/include/sqlpp11/postgresql/result_field.h b/include/sqlpp11/postgresql/result_field.h deleted file mode 100644 index 8bfe1120..00000000 --- a/include/sqlpp11/postgresql/result_field.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once - -/* - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace sqlpp -{ - namespace postgresql - { - // Forward declaration - class connection_base; - } - - namespace detail - { - inline unsigned char unhex(unsigned char c) - { - switch (c) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return c - '0'; - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - return c + 10 - 'a'; - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - return c + 10 - 'A'; - } - throw sqlpp::exception{std::string{"Unexpected hex char: "} + static_cast(c)}; - } - - inline void hex_assign(std::vector& value, const uint8_t* blob, size_t len) - { - value.resize(len / 2 - 1); // unhex - leading chars - size_t val_index = 0; - size_t blob_index = 2; - while (blob_index < len) - { - value[val_index] = static_cast(unhex(blob[blob_index]) << 4) + unhex(blob[blob_index + 1]); - ++val_index; - blob_index += 2; - } - } - } // namespace detail - - template - struct result_field_t> - : public result_field_base> - { - private: - const uint8_t* _blob{nullptr}; // Non-owning - - public: - size_t len{}; - - template - void _bind(Target& target, size_t index) - { - target._bind_blob_result(index, &_blob, &len); - if (_blob) - { - detail::hex_assign(this->_value, _blob, len); - len = this->_value.size(); - } - else - this->_value.clear(); - this->_is_null = (_blob == nullptr); - } - - }; -} // namespace sqlpp diff --git a/include/sqlpp11/result_field.h b/include/sqlpp11/result_field.h deleted file mode 100644 index bcfd965b..00000000 --- a/include/sqlpp11/result_field.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -/* - * 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 - -namespace sqlpp -{ - template - struct result_field_t - { - using X = typename FieldSpec::incorrect; - static_assert(wrong_t::value, "Missing specialization for result_field_t"); - }; - - template - Context& serialize(const result_field_t& t, Context& context) - { - if (t.is_null()) - { - context << "NULL"; - } - else - { - serialize(wrap_operand_t>(t.value()), context); - } - return context; - } - - template - inline std::ostream& operator<<(std::ostream& os, const result_field_t& rf) - { - return serialize(rf, os); - } -} // namespace sqlpp diff --git a/include/sqlpp11/result_field_base.h b/include/sqlpp11/result_field_base.h deleted file mode 100644 index 54a08bbf..00000000 --- a/include/sqlpp11/result_field_base.h +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -/* - * 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. - */ - -#include -#include -#include -#include -#include -#include - -namespace sqlpp -{ - template ::_cpp_value_type> - struct result_field_base - { - using _field_spec_t = FieldSpec; - using _alias_t = typename FieldSpec::_alias_t; - using _cpp_value_type = typename value_type_of::_cpp_value_type; - using _cpp_storage_type = StorageType; - - using _traits = make_traits, - tag::is_result_field, - tag::is_expression>; - - using _nodes = detail::type_vector<>; - using _can_be_null = column_spec_can_be_null_t<_field_spec_t>; - - result_field_base() : _is_valid{false}, _is_null{true}, _value{} - { - } - - bool operator==(const _cpp_value_type& rhs) const - { - return value() == rhs; - } - - bool operator!=(const _cpp_value_type& rhs) const - { - return not operator==(rhs); - } - - void _validate() - { - _is_valid = true; - } - - void _invalidate() - { - _is_valid = false; - _is_null = true; - _value = {}; - } - - bool is_null() const - { - if (not _is_valid) - { - throw exception("accessing is_null in non-existing row"); - } - return _is_null; - } - - _cpp_value_type value() const - { - if (not _is_valid) - { - throw exception("accessing value in non-existing row"); - } - - if (_is_null) - { - return {}; - } - return _value; - } - - operator _cpp_value_type() const - { - return value(); - } - - bool _is_valid; - bool _is_null; - _cpp_storage_type _value; - }; -} // namespace sqlpp diff --git a/include/sqlpp11/result_row.h b/include/sqlpp11/result_row.h index a6241b62..b4129a2a 100644 --- a/include/sqlpp11/result_row.h +++ b/include/sqlpp11/result_row.h @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -43,32 +44,22 @@ namespace sqlpp struct result_row_impl; template - struct result_field : public member_t> + struct result_field : public member_t { - using _field = member_t>; + using _field = member_t; result_field() = default; - void _validate() + template + void _bind_field(Target& target) { - _field::operator()()._validate(); - } - - void _invalidate() - { - _field::operator()()._invalidate(); + target.bind_field(index, _field::operator()()); } template - void _bind(Target& target) + void _read_field(Target& target) { - _field::operator()()._bind(target, index); - } - - template - void _post_bind(Target& target) - { - _field::operator()()._post_bind(target, index); + target.read_field(index, _field::operator()()); } template @@ -90,30 +81,18 @@ namespace sqlpp { result_row_impl() = default; - void _validate() + template + void _bind_fields(Target& target) { using swallow = int[]; - (void)swallow{(result_field::_validate(), 0)...}; - } - - void _invalidate() - { - using swallow = int[]; - (void)swallow{(result_field::_invalidate(), 0)...}; + (void)swallow{(result_field::_bind_field(target), 0)...}; } template - void _bind(Target& target) + void _read_fields(Target& target) { using swallow = int[]; - (void)swallow{(result_field::_bind(target), 0)...}; - } - - template - void _post_bind(Target& target) - { - using swallow = int[]; - (void)swallow{(result_field::_post_bind(target), 0)...}; + (void)swallow{(result_field::_read_field(target), 0)...}; } template @@ -154,13 +133,11 @@ namespace sqlpp void _validate() { - _impl::_validate(); _is_valid = true; } void _invalidate() { - _impl::_invalidate(); _is_valid = false; } @@ -180,15 +157,15 @@ namespace sqlpp } template - void _bind(Target& target) + void _bind_fields(Target& target) { - _impl::_bind(target); + _impl::_bind_fields(target); } template - void _post_bind(Target& target) + void _read_fields(Target& target) { - _impl::_post_bind(target); + _impl::_read_fields(target); } template @@ -223,7 +200,7 @@ namespace sqlpp : public detail::result_row_impl, FieldSpecs...> { using _impl = detail::result_row_impl, FieldSpecs...>; - using _field_type = result_field_t>; + using _field_type = sqlpp::string_view; bool _is_valid{false}; std::vector _dynamic_field_names; @@ -249,22 +226,12 @@ namespace sqlpp void _validate() { - _impl::_validate(); _is_valid = true; - for (auto& field : _dynamic_fields) - { - field.second._validate(); - } } void _invalidate() { - _impl::_invalidate(); _is_valid = false; - for (auto& field : _dynamic_fields) - { - field.second._invalidate(); - } } bool operator==(const dynamic_result_row_t& rhs) const @@ -290,7 +257,7 @@ namespace sqlpp std::size_t index = sizeof...(FieldSpecs); for (const auto& field_name : _dynamic_field_names) { - _dynamic_fields.at(field_name)._bind(target, index); + target.read_field(index, _dynamic_fields.at(field_name)); ++index; } } @@ -303,7 +270,7 @@ namespace sqlpp std::size_t index = sizeof...(FieldSpecs); for (const auto& field_name : _dynamic_field_names) { - _dynamic_fields.at(field_name)._post_bind(target, index); + target.post_read(index, _dynamic_fields.at(field_name)); ++index; } } @@ -313,11 +280,9 @@ namespace sqlpp { _impl::_apply(callable); - std::size_t index = sizeof...(FieldSpecs); for (const auto& field_name : _dynamic_field_names) { - _dynamic_fields.at(field_name)._apply(callable); - ++index; + callable(_dynamic_fields.at(field_name)); } } @@ -326,11 +291,9 @@ namespace sqlpp { _impl::_apply(callable); - std::size_t index = sizeof...(FieldSpecs); for (const auto& field_name : _dynamic_field_names) { - _dynamic_fields.at(field_name)._apply(callable); - ++index; + callable(_dynamic_fields.at(field_name)); } } }; diff --git a/include/sqlpp11/sqlite3/bind_result.h b/include/sqlpp11/sqlite3/bind_result.h index 5fcee824..790606d5 100644 --- a/include/sqlpp11/sqlite3/bind_result.h +++ b/include/sqlpp11/sqlite3/bind_result.h @@ -31,6 +31,9 @@ #include #include #include +#include +#include +#include #include #include @@ -83,7 +86,7 @@ namespace sqlpp { result_row._validate(); } - result_row._bind(*this); + result_row._read_fields(*this); } else { @@ -92,118 +95,119 @@ namespace sqlpp } } - void _bind_boolean_result(size_t index, signed char* value, bool* is_null) + void read_field(size_t index, bool& value) { if (_handle->debug) - std::cerr << "Sqlite3 debug: binding boolean result " << *value << " at index: " << index << std::endl; + std::cerr << "Sqlite3 debug: binding boolean result at index: " << index << std::endl; - *value = static_cast(sqlite3_column_int(_handle->sqlite_statement, static_cast(index))); - *is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast(index)) == SQLITE_NULL; + value = static_cast(sqlite3_column_int(_handle->sqlite_statement, static_cast(index))); } - void _bind_floating_point_result(size_t index, double* value, bool* is_null) + void read_field(size_t index, double& value) { if (_handle->debug) - std::cerr << "Sqlite3 debug: binding floating_point result " << *value << " at index: " << index << std::endl; + std::cerr << "Sqlite3 debug: binding floating_point result at index: " << index << std::endl; switch (sqlite3_column_type(_handle->sqlite_statement, static_cast(index))) { case (SQLITE3_TEXT): - *value = atof( + value = atof( reinterpret_cast(sqlite3_column_text(_handle->sqlite_statement, static_cast(index)))); break; default: - *value = sqlite3_column_double(_handle->sqlite_statement, static_cast(index)); + value = sqlite3_column_double(_handle->sqlite_statement, static_cast(index)); } - *is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast(index)) == SQLITE_NULL; } - void _bind_integral_result(size_t index, int64_t* value, bool* is_null) + void read_field(size_t index, int64_t& value) { if (_handle->debug) - std::cerr << "Sqlite3 debug: binding integral result " << *value << " at index: " << index << std::endl; + std::cerr << "Sqlite3 debug: reading integral result at index: " << index << std::endl; - *value = sqlite3_column_int64(_handle->sqlite_statement, static_cast(index)); - *is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast(index)) == SQLITE_NULL; + value = sqlite3_column_int64(_handle->sqlite_statement, static_cast(index)); } - void _bind_unsigned_integral_result(size_t index, uint64_t* value, bool* is_null) + void read_field(size_t index, uint64_t& value) { if (_handle->debug) - std::cerr << "Sqlite3 debug: binding unsigned integral result " << *value << " at index: " << index - << std::endl; + std::cerr << "Sqlite3 debug: binding unsigned integral result at index: " << index << std::endl; - *value = static_cast(sqlite3_column_int64(_handle->sqlite_statement, static_cast(index))); - *is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast(index)) == SQLITE_NULL; + value = static_cast(sqlite3_column_int64(_handle->sqlite_statement, static_cast(index))); } - void _bind_text_result(size_t index, const char** value, size_t* len) + void read_field(size_t index, sqlpp::string_view& value) { if (_handle->debug) std::cerr << "Sqlite3 debug: binding text result at index: " << index << std::endl; - *value = - (reinterpret_cast(sqlite3_column_text(_handle->sqlite_statement, static_cast(index)))); - *len = static_cast(sqlite3_column_bytes(_handle->sqlite_statement, static_cast(index))); + value = sqlpp::string_view( + reinterpret_cast(sqlite3_column_text(_handle->sqlite_statement, static_cast(index))), + static_cast(sqlite3_column_bytes(_handle->sqlite_statement, static_cast(index)))); } - void _bind_blob_result(size_t index, const uint8_t** value, size_t* len) + void read_field(size_t index, sqlpp::span& value) { if (_handle->debug) - std::cerr << "Sqlite3 debug: binding text result at index: " << index << std::endl; + std::cerr << "Sqlite3 debug: binding blob result at index: " << index << std::endl; - *value = - (reinterpret_cast(sqlite3_column_blob(_handle->sqlite_statement, static_cast(index)))); - *len = static_cast(sqlite3_column_bytes(_handle->sqlite_statement, static_cast(index))); + value = sqlpp::span( + reinterpret_cast(sqlite3_column_blob(_handle->sqlite_statement, static_cast(index))), + static_cast(sqlite3_column_bytes(_handle->sqlite_statement, static_cast(index)))); } - void _bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null) + void read_field(size_t index, ::sqlpp::chrono::day_point& value) { if (_handle->debug) std::cerr << "Sqlite3 debug: binding date result at index: " << index << std::endl; - *value = {}; - *is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast(index)) == SQLITE_NULL; - if (*is_null) - { - return; - } - const auto date_string = reinterpret_cast(sqlite3_column_text(_handle->sqlite_statement, static_cast(index))); if (_handle->debug) std::cerr << "Sqlite3 debug: date string: " << date_string << std::endl; - if (::sqlpp::detail::parse_date(*value, date_string) == false) + if (::sqlpp::detail::parse_date(value, date_string) == false) { + value = {}; if (_handle->debug) std::cerr << "Sqlite3 debug: invalid date result: " << date_string << std::endl; } } - void _bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null) + void read_field(size_t index, ::sqlpp::chrono::microsecond_point& value) { if (_handle->debug) std::cerr << "Sqlite3 debug: binding date result at index: " << index << std::endl; - *value = {}; - *is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast(index)) == SQLITE_NULL; - if (*is_null) - { - return; - } - const auto date_time_string = reinterpret_cast(sqlite3_column_text(_handle->sqlite_statement, static_cast(index))); if (_handle->debug) std::cerr << "Sqlite3 debug: date_time string: " << date_time_string << std::endl; // We treat DATETIME fields as containing either date+time or just date. - if (::sqlpp::detail::parse_date_or_timestamp(*value, date_time_string) == false) + if (::sqlpp::detail::parse_date_or_timestamp(value, date_time_string) == false) { + value = {}; if (_handle->debug) std::cerr << "Sqlite3 debug: invalid date_time result: " << date_time_string << std::endl; } } + template + auto read_field(size_t index, sqlpp::optional& value) -> void + { + const bool is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast(index)) == SQLITE_NULL; + if (is_null) + { + value.reset(); + } + else + { + if (not value) + { + value = T{}; + } + read_field(index, *value); + } + } + private: bool next_impl() { diff --git a/tests/core/serialize/Insert.cpp b/tests/core/serialize/Insert.cpp index d4b59e94..3dd1a763 100644 --- a/tests/core/serialize/Insert.cpp +++ b/tests/core/serialize/Insert.cpp @@ -47,12 +47,11 @@ int Insert(int, char* []) "INSERT INTO tab_bar (beta,gamma) VALUES('cheesecake'," + getTrue() + ")"); compare(__LINE__, insert_into(bar).set(bar.beta = ::sqlpp::null, bar.gamma = true), "INSERT INTO tab_bar (beta,gamma) VALUES(NULL," + getTrue() + ")"); -#if __cplusplus >= 201703L - // string_view argument - std::string_view cheeseCake = "cheesecake"; - compare(__LINE__, insert_into(bar).set(bar.beta = cheeseCake, bar.gamma = true), + sqlpp::string_view cheeseCake = "cheesecake"; + compare(__LINE__, insert_into(bar).set(bar.beta = std::string(cheeseCake), bar.gamma = true), + "INSERT INTO tab_bar (beta,gamma) VALUES('cheesecake'," + getTrue() + ")"); + compare(__LINE__, insert_into(bar).set(bar.beta = sqlpp::string_view(cheeseCake), bar.gamma = true), "INSERT INTO tab_bar (beta,gamma) VALUES('cheesecake'," + getTrue() + ")"); -#endif return 0; } diff --git a/tests/core/serialize/Where.cpp b/tests/core/serialize/Where.cpp index 99a2e7d1..6fe8f7e3 100644 --- a/tests/core/serialize/Where.cpp +++ b/tests/core/serialize/Where.cpp @@ -68,11 +68,12 @@ int Where(int, char*[]) compare(__LINE__, where(is_equal_to_or_null(bar.beta, ::sqlpp::value_or_null("SQL"))), " WHERE (tab_bar.beta='SQL')"); compare(__LINE__, where(is_equal_to_or_null(bar.beta, ::sqlpp::value_or_null(::sqlpp::null))), " WHERE tab_bar.beta IS NULL"); -#if __cplusplus >= 201703L + + // string argument + compare(__LINE__, where(bar.beta == std::string("SQL")), " WHERE (tab_bar.beta='SQL')"); + // string_view argument - std::string_view sqlString = "SQL"; - compare(__LINE__, where(bar.beta == sqlString), " WHERE (tab_bar.beta='SQL')"); -#endif + compare(__LINE__, where(bar.beta == sqlpp::string_view("SQL")), " WHERE (tab_bar.beta='SQL')"); return 0; } diff --git a/tests/core/types/result_row.cpp b/tests/core/types/result_row.cpp index e49e3c0d..e0d3b0d8 100644 --- a/tests/core/types/result_row.cpp +++ b/tests/core/types/result_row.cpp @@ -27,6 +27,8 @@ #include "Sample.h" #include +#include "../../include/test_helpers.h" + namespace { constexpr auto bar = test::TabBar{}; @@ -47,9 +49,9 @@ namespace // result fields are as nullable as the expressions they represent const auto rows = db(select(bar.alpha, bar.gamma, seven).from(bar).unconditionally()); auto& x = rows.front(); - static_assert(sqlpp::can_be_null_t::value, ""); - static_assert(not sqlpp::can_be_null_t::value, ""); - static_assert(not sqlpp::can_be_null_t::value, ""); + static_assert(is_optional::value, ""); + static_assert(not is_optional::value, ""); + static_assert(not is_optional::value, ""); } } @@ -61,20 +63,20 @@ namespace .from(foo.join(bar).on(foo.omega > bar.alpha)) .unconditionally()); auto& x = rows.front(); - static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); - static_assert(not sqlpp::can_be_null_t::value, "left side of (inner) join cannot be null"); - static_assert(not sqlpp::can_be_null_t::value, "right side of (inner) join cannot be null"); - static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + static_assert(is_optional::value, "nullable value can always be null"); + static_assert(not is_optional::value, "left side of (inner) join cannot be null"); + static_assert(not is_optional::value, "right side of (inner) join cannot be null"); + static_assert(not is_optional::value, "constant non-null value can not be null"); } { const auto& rows = db(select(bar.alpha, foo.delta, bar.gamma, seven) .from(bar.join(foo).on(foo.omega > bar.alpha)) .unconditionally()); auto& x = rows.front(); - static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); - static_assert(not sqlpp::can_be_null_t::value, "left side of (inner) join cannot be null"); - static_assert(not sqlpp::can_be_null_t::value, "right side of (inner) join cannot be null"); - static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + static_assert(is_optional::value, "nullable value can always be null"); + static_assert(not is_optional::value, "left side of (inner) join cannot be null"); + static_assert(not is_optional::value, "right side of (inner) join cannot be null"); + static_assert(not is_optional::value, "constant non-null value can not be null"); } { MockSizeDb db2; @@ -91,20 +93,20 @@ namespace .from(foo.inner_join(bar).on(foo.omega > bar.alpha)) .unconditionally()); auto& x = rows.front(); - static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); - static_assert(not sqlpp::can_be_null_t::value, "left side of inner join cannot be null"); - static_assert(not sqlpp::can_be_null_t::value, "right side of inner join cannot be null"); - static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + static_assert(is_optional::value, "nullable value can always be null"); + static_assert(not is_optional::value, "left side of inner join cannot be null"); + static_assert(not is_optional::value, "right side of inner join cannot be null"); + static_assert(not is_optional::value, "constant non-null value can not be null"); } { const auto rows = db(select(bar.alpha, foo.delta, bar.gamma, seven) .from(bar.inner_join(foo).on(foo.omega > bar.alpha)) .unconditionally()); auto& x = rows.front(); - static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); - static_assert(not sqlpp::can_be_null_t::value, "left side of inner join cannot be null"); - static_assert(not sqlpp::can_be_null_t::value, "right side of inner join cannot be null"); - static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + static_assert(is_optional::value, "nullable value can always be null"); + static_assert(not is_optional::value, "left side of inner join cannot be null"); + static_assert(not is_optional::value, "right side of inner join cannot be null"); + static_assert(not is_optional::value, "constant non-null value can not be null"); } // Left outer join @@ -113,20 +115,20 @@ namespace .from(foo.left_outer_join(bar).on(foo.omega > bar.alpha)) .unconditionally()); auto& x = rows.front(); - static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); - static_assert(not sqlpp::can_be_null_t::value, "left side of left outer join cannot be null"); - static_assert(sqlpp::can_be_null_t::value, "right side of left outer join can be null"); - static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + static_assert(is_optional::value, "nullable value can always be null"); + static_assert(not is_optional::value, "left side of left outer join cannot be null"); + static_assert(is_optional::value, "right side of left outer join can be null"); + static_assert(not is_optional::value, "constant non-null value can not be null"); } { const auto rows = db(select(bar.alpha, foo.delta, bar.gamma, seven) .from(bar.left_outer_join(foo).on(foo.omega > bar.alpha)) .unconditionally()); auto& x = rows.front(); - static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); - static_assert(not sqlpp::can_be_null_t::value, "left side of left outer join cannot be null"); - static_assert(sqlpp::can_be_null_t::value, "right side of left outer join can be null"); - static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + static_assert(is_optional::value, "nullable value can always be null"); + static_assert(not is_optional::value, "left side of left outer join cannot be null"); + static_assert(is_optional::value, "right side of left outer join can be null"); + static_assert(not is_optional::value, "constant non-null value can not be null"); } // Right outer join @@ -135,22 +137,22 @@ namespace .from(foo.right_outer_join(bar).on(foo.omega > bar.alpha)) .unconditionally()); auto& x = rows.front(); - static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); - static_assert(sqlpp::can_be_null_t::value, "left side of right outer join can be null"); - static_assert(not sqlpp::can_be_null_t::value, + static_assert(is_optional::value, "nullable value can always be null"); + static_assert(is_optional::value, "left side of right outer join can be null"); + static_assert(not is_optional::value, "right side of right outer join cannot be null"); - static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + static_assert(not is_optional::value, "constant non-null value can not be null"); } { const auto rows = db(select(bar.alpha, foo.delta, bar.gamma, seven) .from(bar.right_outer_join(foo).on(foo.omega > bar.alpha)) .unconditionally()); auto& x = rows.front(); - static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); - static_assert(sqlpp::can_be_null_t::value, "left side of right outer join can be null"); - static_assert(not sqlpp::can_be_null_t::value, + static_assert(is_optional::value, "nullable value can always be null"); + static_assert(is_optional::value, "left side of right outer join can be null"); + static_assert(not is_optional::value, "right side of right outer join cannot be null"); - static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + static_assert(not is_optional::value, "constant non-null value can not be null"); } // Outer join @@ -159,20 +161,20 @@ namespace .from(foo.outer_join(bar).on(foo.omega > bar.alpha)) .unconditionally()); auto& x = rows.front(); - static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); - static_assert(sqlpp::can_be_null_t::value, "left side of outer join can be null"); - static_assert(sqlpp::can_be_null_t::value, "right side of outer join can be null"); - static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + static_assert(is_optional::value, "nullable value can always be null"); + static_assert(is_optional::value, "left side of outer join can be null"); + static_assert(is_optional::value, "right side of outer join can be null"); + static_assert(not is_optional::value, "constant non-null value can not be null"); } { const auto rows = db(select(bar.alpha, foo.delta, bar.gamma, seven) .from(bar.outer_join(foo).on(foo.omega > bar.alpha)) .unconditionally()); auto& x = rows.front(); - static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); - static_assert(sqlpp::can_be_null_t::value, "left side of outer join can be null"); - static_assert(sqlpp::can_be_null_t::value, "right side of outer join can be null"); - static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + static_assert(is_optional::value, "nullable value can always be null"); + static_assert(is_optional::value, "left side of outer join can be null"); + static_assert(is_optional::value, "right side of outer join can be null"); + static_assert(not is_optional::value, "constant non-null value can not be null"); } // Cross join @@ -180,19 +182,19 @@ namespace const auto rows = db(select(bar.alpha, foo.delta, bar.gamma, seven).from(foo.cross_join(bar)).unconditionally()); auto& x = rows.front(); - static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); - static_assert(not sqlpp::can_be_null_t::value, "left side of cross join cannot be null"); - static_assert(not sqlpp::can_be_null_t::value, "right side of cross join cannot be null"); - static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + static_assert(is_optional::value, "nullable value can always be null"); + static_assert(not is_optional::value, "left side of cross join cannot be null"); + static_assert(not is_optional::value, "right side of cross join cannot be null"); + static_assert(not is_optional::value, "constant non-null value can not be null"); } { const auto rows = db(select(bar.alpha, foo.delta, bar.gamma, seven).from(bar.cross_join(foo)).unconditionally()); auto& x = rows.front(); - static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); - static_assert(not sqlpp::can_be_null_t::value, "left side of cross join cannot be null"); - static_assert(not sqlpp::can_be_null_t::value, "right side of cross join cannot be null"); - static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + static_assert(is_optional::value, "nullable value can always be null"); + static_assert(not is_optional::value, "left side of cross join cannot be null"); + static_assert(not is_optional::value, "right side of cross join cannot be null"); + static_assert(not is_optional::value, "constant non-null value can not be null"); } } @@ -205,11 +207,11 @@ namespace static_assert(sqlpp::can_be_null_t::value, ""); const auto rows = db(select(count(a), avg(a), max(a), min(a), sum(a)).from(bar).unconditionally()); auto& x = rows.front(); - static_assert(not sqlpp::can_be_null_t::value, ""); - static_assert(sqlpp::can_be_null_t::value, ""); - static_assert(sqlpp::can_be_null_t::value, ""); - static_assert(sqlpp::can_be_null_t::value, ""); - static_assert(sqlpp::can_be_null_t::value, ""); + static_assert(not is_optional::value, ""); + static_assert(is_optional::value, ""); + static_assert(is_optional::value, ""); + static_assert(is_optional::value, ""); + static_assert(is_optional::value, ""); } { // aggregates of nullable values @@ -218,11 +220,11 @@ namespace static_assert(sqlpp::can_be_null_t::value, ""); const auto rows = db(select(count(o), avg(o), max(o), min(o), sum(o)).from(foo).unconditionally()); auto& x = rows.front(); - static_assert(not sqlpp::can_be_null_t::value, ""); - static_assert(sqlpp::can_be_null_t::value, ""); - static_assert(sqlpp::can_be_null_t::value, ""); - static_assert(sqlpp::can_be_null_t::value, ""); - static_assert(sqlpp::can_be_null_t::value, ""); + static_assert(not is_optional::value, ""); + static_assert(is_optional::value, ""); + static_assert(is_optional::value, ""); + static_assert(is_optional::value, ""); + static_assert(is_optional::value, ""); } } } diff --git a/tests/core/usage/CustomQuery.cpp b/tests/core/usage/CustomQuery.cpp index 1331ae5c..2382e71b 100644 --- a/tests/core/usage/CustomQuery.cpp +++ b/tests/core/usage/CustomQuery.cpp @@ -28,6 +28,7 @@ #include "MockDb.h" #include #include +#include "../../include/test_helpers.h" namespace { diff --git a/tests/core/usage/DateTime.cpp b/tests/core/usage/DateTime.cpp index 56dc78ed..54758c14 100644 --- a/tests/core/usage/DateTime.cpp +++ b/tests/core/usage/DateTime.cpp @@ -28,6 +28,8 @@ #include "MockDb.h" #include +#include "../../include/test_helpers.h" + SQLPP_ALIAS_PROVIDER(now) #if _MSC_FULL_VER >= 190023918 diff --git a/tests/core/usage/Insert.cpp b/tests/core/usage/Insert.cpp index f5fb3cc3..91f7c8bf 100644 --- a/tests/core/usage/Insert.cpp +++ b/tests/core/usage/Insert.cpp @@ -27,9 +27,7 @@ #include "Sample.h" #include "is_regular.h" #include -#if __cplusplus >= 201703L -#include -#endif +#include #include #include #include @@ -109,14 +107,11 @@ int Insert(int, char*[]) prepared_insert.params.delta = sqlpp::value_or_null(17); db(prepared_insert); -#if __cplusplus >= 201703L auto prepared_insert_sv = db.prepare(insert_into(t).set(t.gamma = parameter(t.gamma), t.delta = parameter(t.delta), t.beta = parameter(t.beta))); prepared_insert_sv.params.gamma = true; prepared_insert_sv.params.delta = 17; - std::string_view sv = "string_view"; - prepared_insert_sv.params.beta = sv; + prepared_insert_sv.params.beta = sqlpp::string_view("string_view");; db(prepared_insert_sv); -#endif return 0; } diff --git a/tests/core/usage/Interpret.cpp b/tests/core/usage/Interpret.cpp index 2bc43c65..a0cbbb30 100644 --- a/tests/core/usage/Interpret.cpp +++ b/tests/core/usage/Interpret.cpp @@ -27,7 +27,7 @@ #include "Sample.h" #include -#include +#include "../../include/test_helpers.h" int Interpret(int, char* []) { @@ -171,9 +171,9 @@ int Interpret(int, char* []) for (const auto& row : db(select(all_of(t)).from(t).unconditionally())) { - serialize(row.alpha, printer); - serialize(row.beta, printer); - serialize(row.gamma, printer); + serialize(t.alpha == row.alpha.value(), printer); + serialize(t.beta == row.beta.value(), printer); + serialize(t.gamma == row.gamma, printer); } get_sql_name(t); diff --git a/tests/core/usage/Prepared.cpp b/tests/core/usage/Prepared.cpp index af9cd96b..eed3f62d 100644 --- a/tests/core/usage/Prepared.cpp +++ b/tests/core/usage/Prepared.cpp @@ -28,6 +28,7 @@ #include "is_regular.h" #include #include +#include "../../include/test_helpers.h" int Prepared(int, char* []) { @@ -132,7 +133,10 @@ int Prepared(int, char* []) // Can we prepare a query without parameters? { auto ps = db.prepare(select(all_of(t)).from(t).where((t.beta.like("%")))); - auto res = db(ps); + for (const auto& row : db(ps)) + { + std::cerr << row.alpha << std::endl; + } } // Check that a prepared select is default-constructable diff --git a/tests/core/usage/Result.cpp b/tests/core/usage/Result.cpp index 6853246b..3e98dd59 100644 --- a/tests/core/usage/Result.cpp +++ b/tests/core/usage/Result.cpp @@ -28,6 +28,9 @@ #include "MockDb.h" #include + +#include "../../include/test_helpers.h" + int Result(int, char* []) { MockDb db = {}; @@ -39,28 +42,24 @@ int Result(int, char* []) // Using a non-enforcing db for (const auto& row : db(select(all_of(t), t.beta.like("")).from(t).unconditionally())) { - static_assert(sqlpp::can_be_null_t::value, "row.alpha can be null"); - static_assert(std::is_same::value, "Yikes"); - using T = sqlpp::wrap_operand_t; - static_assert(sqlpp::can_be_null_t::value, "row.alpha can be null"); - static_assert(sqlpp::is_result_field_t::value, "result_fields are not wrapped"); + static_assert(is_optional::value, "row.alpha can be null"); - for (const auto& sub : db(select(all_of(t)).from(t).where(t.alpha == row.alpha))) + for (const auto& sub : db(select(all_of(t)).from(t).where(t.alpha == row.alpha.value()))) { std::cerr << sub.alpha << std::endl; } - db(insert_into(t).set(t.beta = row.beta, t.gamma = false)); + db(insert_into(t).set(t.beta = row.beta.value(), t.gamma = false)); } sqlpp::select((t.alpha + 1).as(t.alpha)).flags(sqlpp::all).from(t); for (const auto& row : db(select(all_of(t)).from(t).unconditionally())) { - static_assert(sqlpp::can_be_null_t::value, "row.alpha can be null"); + static_assert(is_optional::value, "row.alpha can be null"); } for (const auto& row : db(select(all_of(t)).from(t).unconditionally())) { - static_assert(sqlpp::can_be_null_t::value, "row.alpha can be null"); + static_assert(is_optional::value, "row.alpha can be null"); } sqlpp::select((t.alpha + 1).as(t.alpha)).flags(sqlpp::all).from(t); diff --git a/tests/core/usage/Select.cpp b/tests/core/usage/Select.cpp index bb01f965..176ea1ba 100644 --- a/tests/core/usage/Select.cpp +++ b/tests/core/usage/Select.cpp @@ -33,13 +33,14 @@ #include #include #include +#include "../../include/test_helpers.h" template int64_t getColumn(Db&& db, const Column& column) { auto result = db(select(column.as(sqlpp::alias::a)).from(column.table()).unconditionally()); - if (not result.empty()) - return result.front().a; + if (not result.empty() and result.front().a.has_value()) + return result.front().a.value(); else return 0; } @@ -49,15 +50,15 @@ struct to_cerr template auto operator()(const Field& field) const -> void { - std::cerr << get_sql_name(field) << " = " << field << std::endl; + std::cerr << field << std::endl; } }; template void print_row(Row const& row) { - int64_t a = row.alpha; - const std::string b = row.beta; + const sqlpp::optional a = row.alpha; + const sqlpp::optional b = row.beta; std::cout << a << ", " << b << std::endl; } @@ -90,16 +91,16 @@ int Select(int, char*[]) for (const auto& row : db(select(all_of(t)).from(t).unconditionally())) { - int64_t a = row.alpha; - const std::string b = row.beta; + const sqlpp::optional a = row.alpha; + const sqlpp::optional b = row.beta; std::cout << a << ", " << b << std::endl; } for (const auto& row : db(select(all_of(t), t.gamma.as(t)).from(t).where(t.alpha > 7 and trim(t.beta) == "test").for_update())) { - int64_t a = row.alpha; - const std::string b = row.beta; + const sqlpp::optional a = row.alpha; + const sqlpp::optional b = row.beta; const bool g = row.tabBar; std::cout << a << ", " << b << ", " << g << std::endl; } @@ -177,7 +178,7 @@ int Select(int, char*[]) s.order_by.add(t.delta.order(sqlpp::sort_type::desc)); for (const auto& row : db(db.prepare(s))) { - int64_t a = row.alpha; + const sqlpp::optional a = row.alpha; std::cout << a << std::endl; } @@ -187,7 +188,7 @@ int Select(int, char*[]) select(sqlpp::value(7).as(t.alpha)); for (const auto& row : - db(select(sqlpp::case_when(true).then(sqlpp::null).else_(sqlpp::null).as(t.beta)).from(t).unconditionally())) + db(select(sqlpp::case_when(true).then(t.beta).else_(sqlpp::null).as(t.beta)).from(t).unconditionally())) { std::cerr << row.beta << std::endl; } diff --git a/tests/core/usage/SelectType.cpp b/tests/core/usage/SelectType.cpp index 90c6f655..e61c8259 100644 --- a/tests/core/usage/SelectType.cpp +++ b/tests/core/usage/SelectType.cpp @@ -32,6 +32,7 @@ #include #include #include +#include "../../include/test_helpers.h" namespace alias { @@ -345,7 +346,7 @@ int SelectType(int, char*[]) for (const auto& row : db(select(all_of(t)).from(t).unconditionally())) { - int64_t a = row.alpha; + const auto a = row.alpha; std::cout << a << std::endl; } diff --git a/tests/core/usage/With.cpp b/tests/core/usage/With.cpp index c93a2511..6229c455 100644 --- a/tests/core/usage/With.cpp +++ b/tests/core/usage/With.cpp @@ -28,6 +28,7 @@ #include #include #include +#include "../../include/test_helpers.h" int With(int, char*[]) { diff --git a/tests/include/test_helpers.h b/tests/include/test_helpers.h new file mode 100644 index 00000000..1cb83179 --- /dev/null +++ b/tests/include/test_helpers.h @@ -0,0 +1,104 @@ +#pragma once + +/* + * Copyright (c) 2024, 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 + +template +std::ostream& operator<<(std::ostream& os, const std::chrono::time_point& t) +{ + const auto dp = ::sqlpp::chrono::floor<::date::days>(t); + const auto ymd = ::date::year_month_day{dp}; + const auto time = ::date::make_time(t - dp); + os << "TIMESTAMP '" << ymd << ' ' << time << "'"; + return os; +} + +template +std::ostream& operator<<(std::ostream& os, const std::chrono::duration& t) +{ + return os << '\'' << ::date::make_time(t) << '\''; +} + +template +std::ostream& operator<<(std::ostream& os, const sqlpp::optional& t) +{ + if (not t) + return os << "NULL"; + return os << t.value(); +} + +inline 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 +auto require_equal(int line, const L& l, const R& r) -> void +{ + if (l != r) + { + std::cerr << line << ": " << l << " != " << r << std::endl; + throw std::runtime_error("Unexpected result"); + } +} + +template +struct is_optional : public std::false_type{}; + +template +struct is_optional> : public std::true_type{}; + diff --git a/tests/mysql/usage/DateTime.cpp b/tests/mysql/usage/DateTime.cpp index 7f3a0d6c..e98e6233 100644 --- a/tests/mysql/usage/DateTime.cpp +++ b/tests/mysql/usage/DateTime.cpp @@ -28,6 +28,8 @@ #include #include +#include "../../include/test_helpers.h" + #include #include #include @@ -36,20 +38,6 @@ const auto library_raii = sqlpp::mysql::scoped_library_initializer_t{}; namespace { - 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); - std::cerr << "\n" ; - throw std::runtime_error("Unexpected result"); - } - } - template auto require_close(int line, const L& l, const R& r) -> void { @@ -90,13 +78,10 @@ int DateTime(int, char*[]) 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{}); - require_close(__LINE__, row.colDateTimePoint.value(), now); - require_equal(__LINE__, row.colTimeOfDay.is_null(), true); - require_equal(__LINE__, row.colTimeOfDay.value(), ::std::chrono::microseconds{}); + require_equal(__LINE__, row.colDayPoint.has_value(), false); + require_equal(__LINE__, row.colTimePoint.has_value(), false); + require_close(__LINE__, row.colDateTimePoint, now); + require_equal(__LINE__, row.colTimeOfDay.has_value(), false); } db(update(tab).set(tab.colDayPoint = today, tab.colTimePoint = now, tab.colTimeOfDay = current).unconditionally()); @@ -111,8 +96,7 @@ int DateTime(int, char*[]) auto statement = db.prepare(select(all_of(tab)).from(tab).unconditionally()); for (const auto& row : db(statement)) { - require_equal(__LINE__, row.colDateTimePoint.is_null(), false); - require_close(__LINE__, row.colDateTimePoint.value(), now); + require_close(__LINE__, row.colDateTimePoint, now); require_equal(__LINE__, row.colDayPoint.value(), today); require_equal(__LINE__, row.colTimePoint.value(), now); require_close(__LINE__, row.colTimeOfDay.value(), current); diff --git a/tests/mysql/usage/DynamicSelect.cpp b/tests/mysql/usage/DynamicSelect.cpp index 4cad0cb1..5a94c73d 100644 --- a/tests/mysql/usage/DynamicSelect.cpp +++ b/tests/mysql/usage/DynamicSelect.cpp @@ -48,9 +48,9 @@ int DynamicSelect(int, char*[]) auto db = sql::make_test_connection(); db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(CREATE TABLE tab_sample ( - alpha bigint(20) DEFAULT NULL, + alpha bigint(20) AUTO_INCREMENT NOT NULL PRIMARY KEY, beta varchar(255) DEFAULT NULL, - gamma bool DEFAULT NULL + gamma bool NOT NULL DEFAULT 0 ))"); const auto tab = TabSample{}; diff --git a/tests/mysql/usage/Json.cpp b/tests/mysql/usage/Json.cpp index 78d477ba..dfe426fc 100644 --- a/tests/mysql/usage/Json.cpp +++ b/tests/mysql/usage/Json.cpp @@ -78,10 +78,10 @@ int Json(int, char*[]) if (result.empty()) throw std::runtime_error{"selection result is empty"}; - const std::string value = result.front().value; + const sqlpp::optional value = result.front().value; if (value != "value") - throw std::runtime_error{std::string{"unexpected value: "} + value}; + throw std::runtime_error{std::string{"unexpected value: "} + std::string(value ? value.value() : "NULL")}; } catch (const std::exception& e) { diff --git a/tests/mysql/usage/MoveConstructor.cpp b/tests/mysql/usage/MoveConstructor.cpp index cc15505a..9f968034 100644 --- a/tests/mysql/usage/MoveConstructor.cpp +++ b/tests/mysql/usage/MoveConstructor.cpp @@ -50,9 +50,9 @@ int MoveConstructor(int, char*[]) connections.at(0).execute(R"(DROP TABLE IF EXISTS tab_sample)"); connections.at(0).execute(R"(CREATE TABLE tab_sample ( - alpha bigint(20) DEFAULT NULL, + alpha bigint(20) AUTO_INCREMENT NOT NULL PRIMARY KEY, beta varchar(255) DEFAULT NULL, - gamma bool DEFAULT NULL + gamma bool NOT NULL DEFAULT 0 ))"); assert(connections.at(0).is_transaction_active() == false); diff --git a/tests/mysql/usage/Sample.cpp b/tests/mysql/usage/Sample.cpp index 27935eea..6543eae9 100644 --- a/tests/mysql/usage/Sample.cpp +++ b/tests/mysql/usage/Sample.cpp @@ -27,6 +27,7 @@ #include "TabSample.h" #include #include +#include "../../include/test_helpers.h" #include #include @@ -82,9 +83,9 @@ int Sample(int, char*[]) const auto y = db.prepare(x); for (const auto& row : db(db.prepare(select(all_of(tab)).from(tab).unconditionally()))) { - std::cerr << "alpha: " << row.alpha.is_null() << std::endl; - std::cerr << "beta: " << row.beta.is_null() << std::endl; - std::cerr << "gamma: " << row.gamma.is_null() << std::endl; + std::cerr << "alpha: " << row.alpha << std::endl; + std::cerr << "beta: " << row.beta << std::endl; + std::cerr << "gamma: " << row.gamma << std::endl; } db(insert_into(tab).set(tab.beta = "kaesekuchen", tab.gamma = true)); db(insert_into(tab).default_values()); @@ -122,8 +123,8 @@ int Sample(int, char*[]) auto result = db(select(all_of(tab), select(max(tab.alpha)).from(tab)).from(tab).unconditionally()); if (const auto& row = *result.begin()) { - long a = row.alpha; - long m = row.max; + const int64_t a = row.alpha; + const sqlpp::optional m = row.max; std::cerr << __LINE__ << " row.alpha: " << a << ", row.max: " << m << std::endl; } tx.commit(); diff --git a/tests/mysql/usage/Select.cpp b/tests/mysql/usage/Select.cpp index 96eb3e5a..2d6e58ac 100644 --- a/tests/mysql/usage/Select.cpp +++ b/tests/mysql/usage/Select.cpp @@ -34,6 +34,7 @@ #include #include #include +#include "../../include/test_helpers.h" #include #include @@ -91,7 +92,7 @@ int Select(int, char*[]) db.execute(R"(CREATE TABLE tab_sample ( alpha bigint(20) AUTO_INCREMENT, beta varchar(255) DEFAULT NULL, - gamma bool DEFAULT NULL, + gamma bool NOT NULL DEFAULT 0, PRIMARY KEY (alpha) ))"); db.execute(R"(DROP TABLE IF EXISTS tab_foo)"); @@ -153,8 +154,8 @@ int Select(int, char*[]) auto result = db(select(all_of(tab), select(max(tab.alpha)).from(tab)).from(tab).unconditionally()); if (const auto& row = *result.begin()) { - long a = row.alpha; - long m = row.max; + sqlpp::optional a = row.alpha; + sqlpp::optional m = row.max; std::cerr << "-----------------------------" << a << ", " << m << std::endl; } tx.commit(); diff --git a/tests/mysql/usage/Truncated.cpp b/tests/mysql/usage/Truncated.cpp index 2c0fa078..e035cd41 100644 --- a/tests/mysql/usage/Truncated.cpp +++ b/tests/mysql/usage/Truncated.cpp @@ -34,6 +34,7 @@ #include #include #include +#include "../../include/test_helpers.h" #include #include @@ -51,14 +52,13 @@ int Truncated(int, char*[]) auto db = sql::make_test_connection(); db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(CREATE TABLE tab_sample ( - alpha bigint(20) AUTO_INCREMENT, + alpha bigint(20) AUTO_INCREMENT NOT NULL PRIMARY KEY, beta varchar(255) DEFAULT NULL, - gamma bool DEFAULT NULL, - PRIMARY KEY (alpha) + gamma bool NOT NULL DEFAULT 0 ))"); db.execute(R"(DROP TABLE IF EXISTS tab_foo)"); db.execute(R"(CREATE TABLE tab_foo ( - omega bigint(20) DEFAULT NULL + omega bigint(20) NOT NULL ))"); db(insert_into(tab).set(tab.gamma = true, tab.beta = "cheese")); diff --git a/tests/mysql/usage/Union.cpp b/tests/mysql/usage/Union.cpp index a406ac1a..c6d21803 100644 --- a/tests/mysql/usage/Union.cpp +++ b/tests/mysql/usage/Union.cpp @@ -27,6 +27,7 @@ #include "TabSample.h" #include #include +#include "../../include/test_helpers.h" #include diff --git a/tests/postgresql/usage/Blob.cpp b/tests/postgresql/usage/Blob.cpp index 0d3fece7..1c31cd67 100644 --- a/tests/postgresql/usage/Blob.cpp +++ b/tests/postgresql/usage/Blob.cpp @@ -41,35 +41,30 @@ const auto blob = model::BlobSample{}; constexpr size_t blob_size = 1000 * 1000ul; constexpr size_t blob_small_size = 999; -void verify_blob(sql::connection& db, const std::vector& data, uint64_t id) +void verify_blob(sql::connection& db, const std::vector& expected, uint64_t id) { auto result = db(select(blob.data).from(blob).where(blob.id == id)); const auto& result_row = result.front(); - std::cerr << "id: " << id << std::endl; - std::cerr << "Insert size: " << data.size() << std::endl; - std::cerr << "Select size: " << result_row.data.len << std::endl; - if (data.size() != result_row.data.len) + if (!result_row.data) + throw std::runtime_error("blob data is unpexpectedly NULL for id " + std::to_string(id)); + const auto received = *result_row.data; + + if (expected.size() != received.size()) { std::cerr << "Size mismatch" << std::endl; - throw std::runtime_error("Size mismatch " + std::to_string(data.size()) + - " != " + std::to_string(result_row.data.len)); + throw std::runtime_error("Size mismatch " + std::to_string(expected.size()) + + " != " + std::to_string(received.size())); } std::cerr << "Verifying content" << std::endl; - const auto result_blob = result_row.data.value(); - if (data != result_blob) + for (size_t i = 0; i < expected.size(); i++) { - std::cout << "Content mismatch ([row] original -> received)" << std::endl; - - for (size_t i = 0; i < data.size(); i++) + if (expected[i] != received[i]) { - if (data[i] != result_row.data.value()[i]) - { - std::cerr << "[" << i << "] " << static_cast(data.at(i)) << " -> " << static_cast(result_blob.at(i)) - << std::endl; - } + std::cerr << "expected[" << i << "] " << static_cast(expected[i]) << " != received " << static_cast(received[i]) + << std::endl; + throw std::runtime_error("Content mismatch"); } - throw std::runtime_error("Content mismatch"); } } @@ -108,12 +103,10 @@ int Blob(int, char*[]) { auto result = db(select(blob.data).from(blob).where(blob.id == null_id)); const auto& result_row = result.front(); - std::cerr << "Null blob is_null:\t" << std::boolalpha << result_row.data.is_null() << std::endl; - std::cerr << "Null blob len == 0:\t" << std::boolalpha << (result_row.data.len == 0) << std::endl; - std::cerr << "Null blob blob == nullptr:\t" << std::boolalpha << (result_row.data.is_null()) << std::endl; - if (!result_row.data.is_null() || result_row.data.len != 0) + std::cerr << "Null blob is_null:\t" << std::boolalpha << (result_row.data == sqlpp::nullopt) << std::endl; + if (result_row.data.has_value()) { - throw std::runtime_error("Null blob has incorrect values"); + throw std::runtime_error("Expected NULL blob has value"); } } diff --git a/tests/postgresql/usage/Date.cpp b/tests/postgresql/usage/Date.cpp index 5cd40980..c8b77f60 100644 --- a/tests/postgresql/usage/Date.cpp +++ b/tests/postgresql/usage/Date.cpp @@ -93,10 +93,8 @@ int Date(int, char*[]) 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{}); + require_equal(__LINE__, row.colDayPoint.has_value(), false); + require_equal(__LINE__, row.colTimePoint.has_value(), false); } db(update(tab).set(tab.colDayPoint = today, tab.colTimePoint = now).unconditionally()); diff --git a/tests/postgresql/usage/DateTime.cpp b/tests/postgresql/usage/DateTime.cpp index c44d6e0d..63622232 100644 --- a/tests/postgresql/usage/DateTime.cpp +++ b/tests/postgresql/usage/DateTime.cpp @@ -75,12 +75,9 @@ int DateTime(int, char*[]) 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_time.is_null(), true); - require_equal(__LINE__, row.c_time.value(), ::std::chrono::microseconds{}); - require_equal(__LINE__, row.c_timepoint.is_null(), true); - require_equal(__LINE__, row.c_timepoint.value(), ::sqlpp::chrono::microsecond_point{}); + require_equal(__LINE__, row.c_day.has_value(), false); + require_equal(__LINE__, row.c_time.has_value(), false); + require_equal(__LINE__, row.c_timepoint.has_value(), false); } db(update(tab).set(tab.c_day = today, tab.c_time = current, tab.c_timepoint = now).unconditionally()); diff --git a/tests/postgresql/usage/InsertOnConflict.cpp b/tests/postgresql/usage/InsertOnConflict.cpp index af4cc616..0d863a35 100644 --- a/tests/postgresql/usage/InsertOnConflict.cpp +++ b/tests/postgresql/usage/InsertOnConflict.cpp @@ -33,6 +33,7 @@ #include "TabBar.h" #include "TabFoo.h" #include "make_test_connection.h" +#include "../../include/test_helpers.h" namespace sql = sqlpp::postgresql; diff --git a/tests/postgresql/usage/Returning.cpp b/tests/postgresql/usage/Returning.cpp index 5f3a74bc..00cd010c 100644 --- a/tests/postgresql/usage/Returning.cpp +++ b/tests/postgresql/usage/Returning.cpp @@ -2,6 +2,7 @@ #include #include +#include "../../include/test_helpers.h" #include "TabFoo.h" #include "make_test_connection.h" diff --git a/tests/postgresql/usage/Select.cpp b/tests/postgresql/usage/Select.cpp index ee7ae72a..88ef9d89 100644 --- a/tests/postgresql/usage/Select.cpp +++ b/tests/postgresql/usage/Select.cpp @@ -32,6 +32,7 @@ #include "TabFoo.h" #include "make_test_connection.h" +#include "../../include/test_helpers.h" namespace sql = sqlpp::postgresql; model::TabFoo tab = {}; @@ -69,7 +70,7 @@ int Select(int, char*[]) db.execute(R"(DROP TABLE IF EXISTS tabfoo;)"); db.execute(R"(CREATE TABLE tabfoo ( - alpha bigserial NOT NULL, + alpha bigserial, beta smallint, gamma text, c_bool boolean, @@ -112,8 +113,8 @@ int Select(int, char*[]) 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); + assert(not db(select(tab.c_bool).from(tab).where(tab.gamma == "asdfg")).front().c_bool.value()); + assert(not db(select(tab.c_bool).from(tab).where(tab.alpha == 1)).front().c_bool.has_value()); // test diff --git a/tests/postgresql/usage/Transaction.cpp b/tests/postgresql/usage/Transaction.cpp index 22cf989e..54673895 100644 --- a/tests/postgresql/usage/Transaction.cpp +++ b/tests/postgresql/usage/Transaction.cpp @@ -32,56 +32,10 @@ #include #include #include +#include "../../include/test_helpers.h" #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); @@ -95,10 +49,10 @@ int Transaction(int, char*[]) { require_equal(__LINE__, db.is_transaction_active(), false); - auto current_level = db(custom_query(sqlpp::verbatim("show transaction_isolation;")) - .with_result_type_of(select(sqlpp::value("").as(level)))) - .front() - .level; + auto current_level = std::string(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"; diff --git a/tests/postgresql/usage/Type.cpp b/tests/postgresql/usage/Type.cpp index a243c675..22d3824c 100644 --- a/tests/postgresql/usage/Type.cpp +++ b/tests/postgresql/usage/Type.cpp @@ -51,7 +51,7 @@ namespace { // prepare test with timezone db.execute("DROP TABLE IF EXISTS tab_sample"); - db.execute("CREATE TABLE tab_sample (alpha bigint, beta text, gamma bool)"); + db.execute("CREATE TABLE tab_sample (alpha bigint, beta text, gamma bool NOT NULL DEFAULT 'f')"); } } @@ -69,24 +69,20 @@ int Type(int, char*[]) 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); + require_equal(__LINE__, row.alpha.has_value(), false); + require_equal(__LINE__, row.beta.has_value(), false); + require_equal(__LINE__, row.gamma, 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.has_value(), true); require_equal(__LINE__, row.alpha.value(), 10); - require_equal(__LINE__, row.beta.is_null(), false); + require_equal(__LINE__, row.beta.has_value(), true); require_equal(__LINE__, row.beta.value(), "Cookies!"); - require_equal(__LINE__, row.gamma.is_null(), false); - require_equal(__LINE__, row.gamma.value(), true); + require_equal(__LINE__, row.gamma, true); } db(update(tab).set(tab.alpha = 20, tab.beta = "Monster", tab.gamma = false).unconditionally()); @@ -95,7 +91,7 @@ int Type(int, char*[]) { require_equal(__LINE__, row.alpha.value(), 20); require_equal(__LINE__, row.beta.value(), "Monster"); - require_equal(__LINE__, row.gamma.value(), false); + require_equal(__LINE__, row.gamma, false); } auto prepared_update = db.prepare( @@ -113,7 +109,7 @@ int Type(int, char*[]) { require_equal(__LINE__, row.alpha.value(), 30); require_equal(__LINE__, row.beta.value(), "IceCream"); - require_equal(__LINE__, row.gamma.value(), true); + require_equal(__LINE__, row.gamma, true); } } catch (std::exception& e) diff --git a/tests/sqlite3/usage/Attach.cpp b/tests/sqlite3/usage/Attach.cpp index 5e7b5996..78e67056 100644 --- a/tests/sqlite3/usage/Attach.cpp +++ b/tests/sqlite3/usage/Attach.cpp @@ -50,7 +50,7 @@ int Attach(int, char*[]) db.execute(R"(CREATE TABLE tab_sample ( alpha INTEGER PRIMARY KEY, beta varchar(255) DEFAULT NULL, - gamma bool DEFAULT NULL + gamma bool ))"); // Attaching another in-memory database and creating the same table in it @@ -58,7 +58,7 @@ int Attach(int, char*[]) db.execute(R"(CREATE TABLE other.tab_sample ( alpha INTEGER PRIMARY KEY, beta varchar(255) DEFAULT NULL, - gamma bool DEFAULT NULL + gamma bool ))"); auto left = TabSample{}; diff --git a/tests/sqlite3/usage/AutoIncrement.cpp b/tests/sqlite3/usage/AutoIncrement.cpp index a748effa..2dc74485 100644 --- a/tests/sqlite3/usage/AutoIncrement.cpp +++ b/tests/sqlite3/usage/AutoIncrement.cpp @@ -60,7 +60,8 @@ int AutoIncrement(int, char*[]) std::set results; for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) { - results.insert(row.alpha); + if (row.alpha) + results.insert(row.alpha.value()); }; const auto expected = std::set{1, 2, 3}; assert(results == expected); diff --git a/tests/sqlite3/usage/Blob.cpp b/tests/sqlite3/usage/Blob.cpp index c4473f18..45779f95 100644 --- a/tests/sqlite3/usage/Blob.cpp +++ b/tests/sqlite3/usage/Blob.cpp @@ -12,7 +12,7 @@ #include namespace sql = sqlpp::sqlite3; -const auto blob = BlobSample{}; +const auto tab = BlobSample{}; /* * max default blob/text is 1,000,000,000 @@ -23,34 +23,30 @@ const auto blob = BlobSample{}; constexpr size_t blob_size = 1000 * 1000ul; constexpr size_t blob_small_size = 999; -void verify_blob(sql::connection& db, const std::vector& data, uint64_t id) +void verify_blob(sql::connection& db, const std::vector& expected, uint64_t id) { - auto result = db(select(blob.data).from(blob).where(blob.id == id)); + auto result = db(select(tab.data).from(tab).where(tab.id == id)); const auto& result_row = result.front(); - std::cerr << "Insert size: " << data.size() << std::endl; - std::cerr << "Select size: " << result_row.data.len << std::endl; - if (data.size() != result_row.data.len) + if (!result_row.data) + throw std::runtime_error("blob data is unpexpectedly NULL for id " + std::to_string(id)); + const auto received = *result_row.data; + + if (expected.size() != received.size()) { std::cerr << "Size mismatch" << std::endl; - throw std::runtime_error("Size mismatch " + std::to_string(data.size()) + - " != " + std::to_string(result_row.data.len)); + throw std::runtime_error("Size mismatch " + std::to_string(expected.size()) + + " != " + std::to_string(received.size())); } std::cerr << "Verifying content" << std::endl; - std::vector result_blob(result_row.data.blob, result_row.data.blob + result_row.data.len); - if (data != result_blob) + for (size_t i = 0; i < expected.size(); i++) { - std::cout << "Content mismatch ([row] original -> received)" << std::endl; - - for (size_t i = 0; i < data.size(); i++) + if (expected[i] != received[i]) { - if (data[i] != result_row.data.blob[i]) - { - std::cerr << "[" << i << "] " << static_cast(data.at(i)) << " -> " << static_cast(result_blob.at(i)) - << std::endl; - } + std::cerr << "expected[" << i << "] " << static_cast(expected[i]) << " != received " << static_cast(received[i]) + << std::endl; + throw std::runtime_error("Content mismatch"); } - throw std::runtime_error("Content mismatch"); } } @@ -77,26 +73,20 @@ int Blob(int, char*[]) std::generate_n(data_smaller.begin(), blob_small_size, generator); // If we use the bigger blob it will trigger SQLITE_TOOBIG for the query - auto id = db(insert_into(blob).set(blob.data = data_smaller)); + auto id = db(insert_into(tab).set(tab.data = data_smaller)); - auto prepared_insert = db.prepare(insert_into(blob).set(blob.data = parameter(blob.data))); + auto prepared_insert = db.prepare(insert_into(tab).set(tab.data = parameter(tab.data))); prepared_insert.params.data = data; - auto prep_id = db(prepared_insert); + const auto prep_id = db(prepared_insert); prepared_insert.params.data.set_null(); - auto null_id = db(prepared_insert); + const auto null_id = db(prepared_insert); verify_blob(db, data_smaller, id); verify_blob(db, data, prep_id); { - auto result = db(select(blob.data).from(blob).where(blob.id == null_id)); + auto result = db(select(tab.data).from(tab).where(tab.id == null_id)); const auto& result_row = result.front(); - std::cerr << "Null blob is_null:\t" << std::boolalpha << result_row.data.is_null() << std::endl; - std::cerr << "Null blob len == 0:\t" << std::boolalpha << (result_row.data.len == 0) << std::endl; - std::cerr << "Null blob blob == nullptr:\t" << std::boolalpha << (result_row.data.blob == nullptr) << std::endl; - if (!result_row.data.is_null() || result_row.data.len != 0 || result_row.data.blob != nullptr) - { - throw std::runtime_error("Null blob has incorrect values"); - } + std::cerr << "Null blob is_null:\t" << std::boolalpha << (result_row.data == sqlpp::nullopt) << std::endl; } return 0; } diff --git a/tests/sqlite3/usage/DateTime.cpp b/tests/sqlite3/usage/DateTime.cpp index 7a95e432..1895b46c 100644 --- a/tests/sqlite3/usage/DateTime.cpp +++ b/tests/sqlite3/usage/DateTime.cpp @@ -78,10 +78,8 @@ int DateTime(int, char*[]) 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{}); + require_equal(__LINE__, row.colDayPoint == sqlpp::nullopt, true); + require_equal(__LINE__, row.colTimePoint == sqlpp::nullopt, true); } db(update(tab).set(tab.colDayPoint = today, tab.colTimePoint = now).unconditionally()); diff --git a/tests/sqlite3/usage/DynamicLoadingTest.cpp b/tests/sqlite3/usage/DynamicLoadingTest.cpp index 7677a7f9..b008a4bd 100644 --- a/tests/sqlite3/usage/DynamicLoadingTest.cpp +++ b/tests/sqlite3/usage/DynamicLoadingTest.cpp @@ -57,7 +57,7 @@ int main() db.execute("CREATE TABLE tab_sample (\ alpha bigint(20) DEFAULT NULL,\ beta varchar(255) DEFAULT NULL,\ - gamma bool DEFAULT NULL\ + gamma bool\ )"); const auto tab = TabSample{}; diff --git a/tests/sqlite3/usage/DynamicSelect.cpp b/tests/sqlite3/usage/DynamicSelect.cpp index 4ca75d50..1552362e 100644 --- a/tests/sqlite3/usage/DynamicSelect.cpp +++ b/tests/sqlite3/usage/DynamicSelect.cpp @@ -41,6 +41,13 @@ #include #include +template +std::ostream& operator<<(std::ostream& os, const sqlpp::optional& t) { + if (not t) + return os << "NULL"; + return os << t.value(); +} + SQLPP_ALIAS_PROVIDER(left) namespace sql = sqlpp::sqlite3; @@ -55,7 +62,7 @@ int DynamicSelect(int, char*[]) db.execute("CREATE TABLE tab_sample (\ alpha bigint(20) DEFAULT NULL,\ beta varchar(255) DEFAULT NULL,\ - gamma bool DEFAULT NULL\ + gamma bool\ )"); const auto tab = TabSample{}; diff --git a/tests/sqlite3/usage/FloatingPoint.cpp b/tests/sqlite3/usage/FloatingPoint.cpp index d98723e7..b7a6e356 100644 --- a/tests/sqlite3/usage/FloatingPoint.cpp +++ b/tests/sqlite3/usage/FloatingPoint.cpp @@ -39,15 +39,22 @@ namespace sql = sqlpp::sqlite3; const auto fp = FpSample{}; +template +std::ostream& operator<<(std::ostream& os, const sqlpp::optional& t) { + if (not t) + return os << "NULL"; + return os << t.value(); +} + 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 << l; std::cerr << " != "; - serialize(::sqlpp::wrap_operand_t{r}, std::cerr); + std::cerr << r; throw std::runtime_error("Unexpected result"); } } diff --git a/tests/sqlite3/usage/Sample.cpp b/tests/sqlite3/usage/Sample.cpp index 61fb9169..c2bc8448 100644 --- a/tests/sqlite3/usage/Sample.cpp +++ b/tests/sqlite3/usage/Sample.cpp @@ -40,6 +40,13 @@ SQLPP_ALIAS_PROVIDER(pragma) SQLPP_ALIAS_PROVIDER(sub) +template +std::ostream& operator<<(std::ostream& os, const sqlpp::optional& t) { + if (not t) + return os << "NULL"; + return os << t.value(); +} + namespace sql = sqlpp::sqlite3; int Sample(int, char*[]) { @@ -52,7 +59,7 @@ int Sample(int, char*[]) db.execute(R"(CREATE TABLE tab_sample ( alpha INTEGER PRIMARY KEY, beta varchar(255) DEFAULT NULL, - gamma bool DEFAULT NULL + gamma bool ))"); db.execute(R"(CREATE TABLE tab_foo ( omega bigint(20) DEFAULT NULL @@ -102,8 +109,8 @@ int Sample(int, char*[]) .from(tab) .unconditionally())) { - int64_t x = row.alpha; - int64_t a = row.max; + sqlpp::optional x = row.alpha; + sqlpp::optional a = row.max; std::cout << x << ", " << a << std::endl; } tx.commit(); @@ -134,7 +141,8 @@ int Sample(int, char*[]) } std::cerr << "--------" << std::endl; - ps.params.alpha = sqlpp::eval(db, "last_insert_rowid()"); + const auto last_id = sqlpp::eval(db, "last_insert_rowid()"); + ps.params.alpha = last_id.value(); ps.params.gamma = false; for (const auto& row : db(ps)) { diff --git a/tests/sqlite3/usage/Select.cpp b/tests/sqlite3/usage/Select.cpp index 4001d473..92415610 100644 --- a/tests/sqlite3/usage/Select.cpp +++ b/tests/sqlite3/usage/Select.cpp @@ -41,6 +41,13 @@ namespace sql = sqlpp::sqlite3; const auto tab = TabSample{}; +template +std::ostream& operator<<(std::ostream& os, const sqlpp::optional& t) { + if (not t) + return os << "NULL"; + return os << t.value(); +} + void testSelectAll(sql::connection& db, size_t expectedRowCount) { std::cerr << "--------------------------------------" << std::endl; @@ -50,7 +57,7 @@ void testSelectAll(sql::connection& db, size_t expectedRowCount) ++i; std::cerr << ">>> row.alpha: " << row.alpha << ", row.beta: " << row.beta << ", row.gamma: " << row.gamma << std::endl; - assert(static_cast(row.alpha) == i); + assert(row.alpha == static_cast(i)); }; assert(i == expectedRowCount); @@ -61,7 +68,7 @@ void testSelectAll(sql::connection& db, size_t expectedRowCount) ++i; std::cerr << ">>> row.alpha: " << row.alpha << ", row.beta: " << row.beta << ", row.gamma: " << row.gamma << std::endl; - assert(static_cast(row.alpha) == i); + assert(row.alpha == static_cast(i)); }; assert(i == expectedRowCount); std::cerr << "--------------------------------------" << std::endl; @@ -93,7 +100,7 @@ int Select(int, char*[]) db.execute(R"(CREATE TABLE tab_sample ( alpha INTEGER PRIMARY KEY, beta varchar(255) DEFAULT NULL, - gamma bool DEFAULT NULL + gamma bool ))"); testSelectAll(db, 0); @@ -141,8 +148,8 @@ int Select(int, char*[]) auto tx = start_transaction(db); for (const auto& row : db(select(all_of(tab), select(max(tab.alpha)).from(tab)).from(tab).unconditionally())) { - const int64_t x = row.alpha; - const int64_t a = row.max; + const auto x = row.alpha; + const auto a = row.max; std::cout << ">>>" << x << ", " << a << std::endl; } for (const auto& row : @@ -153,14 +160,14 @@ int Select(int, char*[]) std::cerr << ">>> row.alpha: " << row.alpha << ", row.beta: " << row.beta << ", row.gamma: " << row.gamma << ", row.trim: '" << row.trim << "'" << std::endl; // check trim - assert(string_util::trim(row.beta.value()) == row.trim.value()); + assert((not row.beta and not row.trim) || string_util::trim(std::string(row.beta.value())) == row.trim.value()); // end }; for (const auto& row : db(select(all_of(tab), select(trim(tab.beta)).from(tab)).from(tab).unconditionally())) { - const int64_t x = row.alpha; - const std::string a = row.trim; + const sqlpp::optional x = row.alpha; + const sqlpp::optional a = row.trim; std::cout << ">>>" << x << ", " << a << std::endl; } diff --git a/tests/sqlite3/usage/TabSample.sql b/tests/sqlite3/usage/TabSample.sql index fa14c9e4..c3efbb5f 100644 --- a/tests/sqlite3/usage/TabSample.sql +++ b/tests/sqlite3/usage/TabSample.sql @@ -1,5 +1,5 @@ CREATE TABLE tab_sample ( alpha bigint(20) DEFAULT NULL, beta tinyint(1) DEFAULT NULL, - gamma varchar(255) DEFAULT NULL + gamma varchar(255) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; diff --git a/tests/sqlite3/usage/Union.cpp b/tests/sqlite3/usage/Union.cpp index 7209f388..1452844d 100644 --- a/tests/sqlite3/usage/Union.cpp +++ b/tests/sqlite3/usage/Union.cpp @@ -37,6 +37,13 @@ namespace sql = sqlpp::sqlite3; const auto tab = TabSample{}; +template +std::ostream& operator<<(std::ostream& os, const sqlpp::optional& t) { + if (not t) + return os << "NULL"; + return os << t.value(); +} + int Union(int, char*[]) { sql::connection_config config; @@ -48,7 +55,7 @@ int Union(int, char*[]) db.execute(R"(CREATE TABLE tab_sample ( alpha INTEGER PRIMARY KEY, beta varchar(255) DEFAULT NULL, - gamma bool DEFAULT NULL + gamma bool ))"); auto u = select(all_of(tab)).from(tab).unconditionally().union_all(select(all_of(tab)).from(tab).unconditionally()); diff --git a/tests/sqlite3/usage/With.cpp b/tests/sqlite3/usage/With.cpp index c8c51347..4a13beb2 100644 --- a/tests/sqlite3/usage/With.cpp +++ b/tests/sqlite3/usage/With.cpp @@ -39,6 +39,13 @@ namespace sql = sqlpp::sqlite3; const auto tab = TabSample{}; +template +std::ostream& operator<<(std::ostream& os, const sqlpp::optional& t) { + if (not t) + return os << "NULL"; + return os << t.value(); +} + int With(int, char*[]) { #if SQLITE_VERSION_NUMBER >= 3008003 @@ -51,7 +58,7 @@ int With(int, char*[]) db.execute(R"(CREATE TABLE tab_sample ( alpha INTEGER PRIMARY KEY, beta varchar(255) DEFAULT NULL, - gamma bool DEFAULT NULL + gamma bool ))"); auto a = sqlpp::cte(sqlpp::alias::a).as(select(all_of(tab)).from(tab).where(tab.alpha > 3));