diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dd3e7ea..32fd43cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ set(include_dir "${PROJECT_SOURCE_DIR}/include") file(GLOB_RECURSE sqlpp_headers ${include_dir}/*.h) include_directories(${include_dir}) add_subdirectory(tests) +add_subdirectory(test_constraints) install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/sqlpp11" DESTINATION include) diff --git a/include/sqlpp11/all_of.h b/include/sqlpp11/all_of.h index b4d90fae..0af691a4 100644 --- a/include/sqlpp11/all_of.h +++ b/include/sqlpp11/all_of.h @@ -58,7 +58,7 @@ namespace sqlpp static Context& _(const T& t, const Context&) { - static_assert(wrong_t::value, "all_of(table) does not seem to be used in select"); + static_assert(wrong_t::value, "all_of(table) does not seem to be used in select"); } }; diff --git a/include/sqlpp11/assignment.h b/include/sqlpp11/assignment.h index 36938311..4594f663 100644 --- a/include/sqlpp11/assignment.h +++ b/include/sqlpp11/assignment.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -70,9 +71,8 @@ namespace sqlpp static Context& _(const T& t, Context& context) { - if (((trivial_value_is_null_t::value or is_tvin_t::value) - and is_trivial(t._rhs)) - or (std::is_same::value)) + if ((trivial_value_is_null_t::value and rhs_is_trivial(t)) + or rhs_is_null(t)) { serialize(simple_column(t._lhs), context); context << "=NULL"; diff --git a/include/sqlpp11/boolean.h b/include/sqlpp11/boolean.h index 0ed43af3..2c65f798 100644 --- a/include/sqlpp11/boolean.h +++ b/include/sqlpp11/boolean.h @@ -32,215 +32,185 @@ #include #include #include +#include namespace sqlpp { - // boolean operators - namespace detail + // boolean value type + struct boolean { - // boolean value type - struct boolean + using _traits = make_traits; + using _tag = ::sqlpp::tag::is_boolean; + using _cpp_value_type = bool; + + struct _parameter_t { - using _traits = make_traits; - using _tag = ::sqlpp::tag::is_boolean; - using _cpp_value_type = bool; + using _value_type = boolean; // FIXME - struct _parameter_t + _parameter_t(): + _value(false), + _is_null(true) + {} + + _parameter_t(const _cpp_value_type& value): + _value(value), + _is_null(false) + {} + + _parameter_t& operator=(const _cpp_value_type& value) { - using _value_type = boolean; // FIXME + _value = value; + _is_null = (false); + return *this; + } - _parameter_t(): - _value(false), - _is_null(true) - {} - - _parameter_t(const _cpp_value_type& value): - _value(value), - _is_null(false) - {} - - _parameter_t& operator=(const _cpp_value_type& value) - { - _value = value; - _is_null = (false); - return *this; - } - - _parameter_t& operator=(const std::nullptr_t&) - { - _value = false; - _is_null = true; - return *this; - } - - bool is_null() const - { - return _is_null; - } - - _cpp_value_type value() const - { - return _value; - } - - operator _cpp_value_type() const { return value(); } - - template - void _bind(Target& target, size_t index) const - { - target._bind_boolean_parameter(index, &_value, _is_null); - } - - private: - signed char _value; - bool _is_null; - }; - - template - struct _result_field_t; - - // I am SO waiting for concepts lite! - template - struct field_methods_t - { - static constexpr bool _null_is_trivial = true; - operator _cpp_value_type() const { return static_cast(*this).value(); } - }; - - template - struct field_methods_t< - _result_field_t, - typename std::enable_if::value - and column_spec_can_be_null_t::value - and not null_is_trivial_value_t::value>::type> - { - static constexpr bool _null_is_trivial = false; - }; - - template - struct _result_field_t: public field_methods_t<_result_field_t> - { - using _field_methods_t = field_methods_t<_result_field_t>; - - using _traits = make_traits>; - - struct _recursive_traits - { - using _parameters = std::tuple<>; - using _provided_tables = detail::type_set<>; - using _provided_outer_tables = detail::type_set<>; - using _required_tables = detail::type_set<>; - using _extra_tables = detail::type_set<>; - using _can_be_null = column_spec_can_be_null_t; - }; - - _result_field_t(): - _is_valid(false), - _is_null(true), - _value(false) - {} - - void _validate() - { - _is_valid = true; - } - - void _invalidate() - { - _is_valid = false; - _is_null = true; - _value = 0; - } - - 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) - { - if (enforce_null_result_treatment_t::value and not null_is_trivial_value_t::value) - { - throw exception("accessing value of NULL field"); - } - else - { - return false; - } - } - return _value; - } - - template - void _bind(Target& target, size_t i) - { - target._bind_boolean_result(i, &_value, &_is_null); - } - - private: - bool _is_valid; - bool _is_null; - signed char _value; - }; - - template - struct _is_valid_operand - { - static constexpr bool value = - is_expression_t::value // expressions are OK - and is_boolean_t::value // the correct value type is required, of course - ; - }; - - template - struct expression_operators: public basic_expression_operators + _parameter_t& operator=(const std::nullptr_t&) { - template - logical_and_t> operator and(T t) const - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs operand"); + _value = false; + _is_null = true; + return *this; + } - return { *static_cast(this), rhs{t} }; - } + bool is_null() const + { + return _is_null; + } - template - logical_or_t> operator or(T t) const - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs operand"); + _cpp_value_type value() const + { + return _value; + } - return { *static_cast(this), rhs{t} }; - } + operator _cpp_value_type() const { return value(); } - logical_not_t operator not() const + template + void _bind(Target& target, size_t index) const { - return { *static_cast(this) }; + target._bind_boolean_parameter(index, &_value, _is_null); } - }; - template - struct column_operators - { - }; + private: + signed char _value; + bool _is_null; }; - template - inline std::ostream& operator<<(std::ostream& os, const boolean::_result_field_t& e) + template + struct _is_valid_operand { - return os << e.value(); - } - } + static constexpr bool value = + is_expression_t::value // expressions are OK + and is_boolean_t::value // the correct value type is required, of course + ; + }; - using boolean = detail::boolean; + template + struct expression_operators: public basic_expression_operators + { + template + logical_and_t> operator and(T t) const + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs operand"); + + return { *static_cast(this), rhs{t} }; + } + + template + logical_or_t> operator or(T t) const + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs operand"); + + return { *static_cast(this), rhs{t} }; + } + + logical_not_t operator not() const + { + return { *static_cast(this) }; + } + }; + + template + struct column_operators + { + }; + }; + + template + struct result_field_t: public result_field_methods_t> + { + static_assert(std::is_same, boolean>::value, "field type mismatch"); + using _cpp_value_type = typename boolean::_cpp_value_type; + + result_field_t(): + _is_valid(false), + _is_null(true), + _value(false) + {} + + void _validate() + { + _is_valid = true; + } + + void _invalidate() + { + _is_valid = false; + _is_null = true; + _value = 0; + } + + bool is_null() const + { + if (not _is_valid) + throw exception("accessing is_null in non-existing row"); + return _is_null; + } + + bool _is_trivial() const + { + if (not _is_valid) + throw exception("accessing is_null in non-existing row"); + + return value() == false; + } + + _cpp_value_type value() const + { + if (not _is_valid) + throw exception("accessing value in non-existing row"); + + if (_is_null) + { + if (enforce_null_result_treatment_t::value and not null_is_trivial_value_t::value) + { + throw exception("accessing value of NULL field"); + } + else + { + return false; + } + } + return _value; + } + + template + void _bind(Target& target, size_t i) + { + target._bind_boolean_result(i, &_value, &_is_null); + } + + private: + bool _is_valid; + bool _is_null; + signed char _value; + }; + + template + inline std::ostream& operator<<(std::ostream& os, const result_field_t& e) + { + return serialize(e, os); + } } #endif diff --git a/include/sqlpp11/boolean_expression.h b/include/sqlpp11/boolean_expression.h new file mode 100644 index 00000000..b2c66acc --- /dev/null +++ b/include/sqlpp11/boolean_expression.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2013-2014, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_BOOLEAN_EXPRESSION_H +#define SQLPP_BOOLEAN_EXPRESSION_H + +#include +#include + +namespace sqlpp +{ + template + struct boolean_expression_t + { + using _traits = make_traits; + using _recursive_traits = make_recursive_traits<>; + + template + boolean_expression_t(Expression expression): + _expression(expression) + { + static_assert(is_expression_t::value, "boolean_expression requires a boolean expression argument"); + static_assert(is_boolean_t::value, "boolean_expression requires a boolean expression argument"); + } + + boolean_expression_t(const boolean_expression_t&) = default; + boolean_expression_t(boolean_expression_t&&) = default; + boolean_expression_t& operator=(const boolean_expression_t&) = default; + boolean_expression_t& operator=(boolean_expression_t&&) = default; + ~boolean_expression_t() = default; + + interpretable_t _expression; + }; + + template + boolean_expression_t boolean_expression(const Database&, T t) + { + return {t}; + } + + template + boolean_expression_t boolean_expression(T t) + { + return {t}; + } + + template + struct serializer_t> + { + using T = boolean_expression_t; + + static Context& _(const T& t, Context& context) + { + return serialize(t._expression); + } + }; + +} + +#endif diff --git a/include/sqlpp11/count.h b/include/sqlpp11/count.h index a2970de1..814dcf22 100644 --- a/include/sqlpp11/count.h +++ b/include/sqlpp11/count.h @@ -33,10 +33,10 @@ namespace sqlpp { template - struct count_t: public sqlpp::detail::integral::template expression_operators>, + struct count_t: public sqlpp::integral::template expression_operators>, public alias_operators> { - using _traits = make_traits<::sqlpp::detail::integral, ::sqlpp::tag::is_expression, ::sqlpp::tag::is_named_expression>; + using _traits = make_traits<::sqlpp::integral, ::sqlpp::tag::is_expression, ::sqlpp::tag::is_named_expression>; using _recursive_traits = make_recursive_traits; static_assert(is_noop::value or std::is_same::value, "count() used with flag other than 'distinct'"); diff --git a/include/sqlpp11/detail/copy_tuple_args.h b/include/sqlpp11/detail/copy_tuple_args.h index 3496a2be..32631e01 100644 --- a/include/sqlpp11/detail/copy_tuple_args.h +++ b/include/sqlpp11/detail/copy_tuple_args.h @@ -57,7 +57,7 @@ namespace sqlpp template class Target, typename First, typename T> struct copy_tuple_args_impl { - static_assert(wrong_t::value, "copy_tuple_args must be called with a tuple"); + static_assert(wrong_t::value, "copy_tuple_args must be called with a tuple"); }; template class Target, typename First, typename... Args> diff --git a/include/sqlpp11/detail/type_set.h b/include/sqlpp11/detail/type_set.h index 1e95cc6a..4f6f1ef4 100644 --- a/include/sqlpp11/detail/type_set.h +++ b/include/sqlpp11/detail/type_set.h @@ -73,7 +73,7 @@ namespace sqlpp template struct is_element_of { - static_assert(::sqlpp::wrong_t::value, "SET has to be a type set"); + static_assert(::sqlpp::wrong_t::value, "SET has to be a type set"); }; template @@ -85,7 +85,7 @@ namespace sqlpp template struct joined_set { - static_assert(::sqlpp::wrong_t::value, "L and R have to be type sets"); + static_assert(::sqlpp::wrong_t::value, "L and R have to be type sets"); }; template @@ -100,7 +100,7 @@ namespace sqlpp template struct is_superset_of { - static_assert(::sqlpp::wrong_t::value, "L and R have to be type sets"); + static_assert(::sqlpp::wrong_t::value, "L and R have to be type sets"); }; template @@ -118,7 +118,7 @@ namespace sqlpp template struct is_disjunct_from { - static_assert(::sqlpp::wrong_t::value, "invalid argument for is_disjunct_from"); + static_assert(::sqlpp::wrong_t::value, "invalid argument for is_disjunct_from"); }; template @@ -174,7 +174,7 @@ namespace sqlpp template struct make_joined_set { - static_assert(::sqlpp::wrong_t::value, "invalid argument for joined set"); + static_assert(::sqlpp::wrong_t::value, "invalid argument for joined set"); }; template<> @@ -197,7 +197,7 @@ namespace sqlpp template struct make_difference_set { - static_assert(::sqlpp::wrong_t::value, "invalid argument for difference set"); + static_assert(::sqlpp::wrong_t::value, "invalid argument for difference set"); }; template @@ -214,14 +214,14 @@ namespace sqlpp template struct make_intersect_set { - static_assert(::sqlpp::wrong_t::value, "invalid argument for intersect set"); + static_assert(::sqlpp::wrong_t::value, "invalid argument for intersect set"); }; template struct make_intersect_set, type_set> { template - using is_in_both = is_element_of>; + using is_in_both = all_t>::value, is_element_of>::value>; using type = typename make_type_set_if::type; }; @@ -232,7 +232,7 @@ namespace sqlpp template class Transformation, typename T> struct transform_set { - static_assert(::sqlpp::wrong_t::value, "invalid argument for transform_set"); + static_assert(::sqlpp::wrong_t::value, "invalid argument for transform_set"); }; template class Transformation, typename... E> diff --git a/include/sqlpp11/expression.h b/include/sqlpp11/expression.h index c856b56f..73b6c3a8 100644 --- a/include/sqlpp11/expression.h +++ b/include/sqlpp11/expression.h @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include #include @@ -38,7 +40,7 @@ namespace sqlpp { template - struct binary_expression_t: public ::sqlpp::detail::boolean::template expression_operators>, + struct binary_expression_t: public ::sqlpp::boolean::template expression_operators>, public alias_operators> { using _traits = make_traits; @@ -70,7 +72,8 @@ namespace sqlpp { context << "("; serialize(t._lhs, context); - if (rhs_is_null(t)) + if ((trivial_value_is_null_t::value and rhs_is_trivial(t)) + or rhs_is_null(t)) { context << " IS NULL"; } @@ -85,7 +88,7 @@ namespace sqlpp }; template - struct binary_expression_t: public ::sqlpp::detail::boolean::template expression_operators>, + struct binary_expression_t: public ::sqlpp::boolean::template expression_operators>, public alias_operators> { using _traits = make_traits; @@ -117,7 +120,8 @@ namespace sqlpp { context << "("; serialize(t._lhs, context); - if (rhs_is_null(t)) + if ((trivial_value_is_null_t::value and rhs_is_trivial(t)) + or rhs_is_null(t)) { context << " IS NOT NULL"; } @@ -132,7 +136,7 @@ namespace sqlpp }; template - struct unary_expression_t: public ::sqlpp::detail::boolean::template expression_operators>, + struct unary_expression_t: public ::sqlpp::boolean::template expression_operators>, public alias_operators> { using _traits = make_traits; @@ -161,7 +165,7 @@ namespace sqlpp context << "("; if (trivial_value_is_null_t::value) { - serialize(t._lhs, context); + serialize(t._rhs, context); context << " IS NULL "; } else @@ -170,6 +174,8 @@ namespace sqlpp serialize(t._rhs, context); } context << ")"; + + return context; } }; diff --git a/include/sqlpp11/expression_fwd.h b/include/sqlpp11/expression_fwd.h index 2a389e5b..71958635 100644 --- a/include/sqlpp11/expression_fwd.h +++ b/include/sqlpp11/expression_fwd.h @@ -29,64 +29,61 @@ namespace sqlpp { - namespace detail - { - struct boolean; - struct integral; - struct floating_point; - } + struct boolean; + struct integral; + struct floating_point; namespace op { struct less { - using _traits = make_traits<::sqlpp::detail::boolean>; + using _traits = make_traits<::sqlpp::boolean>; static constexpr const char* _name = "<"; }; struct less_equal { - using _traits = make_traits<::sqlpp::detail::boolean>; + using _traits = make_traits<::sqlpp::boolean>; static constexpr const char* _name = "<="; }; struct equal_to { - using _traits = make_traits<::sqlpp::detail::boolean>; + using _traits = make_traits<::sqlpp::boolean>; }; struct not_equal_to { - using _traits = make_traits<::sqlpp::detail::boolean>; + using _traits = make_traits<::sqlpp::boolean>; }; struct greater_equal { - using _traits = make_traits<::sqlpp::detail::boolean>; + using _traits = make_traits<::sqlpp::boolean>; static constexpr const char* _name = ">="; }; struct greater { - using _traits = make_traits<::sqlpp::detail::boolean>; + using _traits = make_traits<::sqlpp::boolean>; static constexpr const char* _name = ">"; }; struct logical_or { - using _traits = make_traits<::sqlpp::detail::boolean>; + using _traits = make_traits<::sqlpp::boolean>; static constexpr const char* _name = " OR "; }; struct logical_and { - using _traits = make_traits<::sqlpp::detail::boolean>; + using _traits = make_traits<::sqlpp::boolean>; static constexpr const char* _name = " AND "; }; struct logical_not { - using _traits = make_traits<::sqlpp::detail::boolean>; + using _traits = make_traits<::sqlpp::boolean>; }; template @@ -112,13 +109,13 @@ namespace sqlpp struct divides { - using _traits = make_traits<::sqlpp::detail::floating_point>; + using _traits = make_traits<::sqlpp::floating_point>; static constexpr const char* _name = "/"; }; struct modulus { - using _traits = make_traits<::sqlpp::detail::integral>; + using _traits = make_traits<::sqlpp::integral>; static constexpr const char* _name = "%"; }; diff --git a/include/sqlpp11/floating_point.h b/include/sqlpp11/floating_point.h index da5f3943..424ac555 100644 --- a/include/sqlpp11/floating_point.h +++ b/include/sqlpp11/floating_point.h @@ -31,271 +31,241 @@ #include #include #include +#include namespace sqlpp { - namespace detail + // floating_point value type + struct floating_point { + using _traits = make_traits; + using _tag = ::sqlpp::tag::is_floating_point; + using _cpp_value_type = double; - // floating_point value type - struct floating_point + struct _parameter_t { - using _traits = make_traits; - using _tag = ::sqlpp::tag::is_floating_point; - using _cpp_value_type = double; + using _value_type = floating_point; - struct _parameter_t + _parameter_t(): + _value(0), + _is_null(true) + {} + + _parameter_t(const _cpp_value_type& value): + _value(value), + _is_null(false) + {} + + _parameter_t& operator=(const _cpp_value_type& value) { - using _value_type = floating_point; + _value = value; + _is_null = false; + return *this; + } - _parameter_t(): - _value(0), - _is_null(true) - {} - - _parameter_t(const _cpp_value_type& value): - _value(value), - _is_null(false) - {} - - _parameter_t& operator=(const _cpp_value_type& value) - { - _value = value; - _is_null = false; - return *this; - } - - _parameter_t& operator=(const std::nullptr_t&) - { - _value = 0; - _is_null = true; - return *this; - } - - bool is_null() const - { - return _is_null; - } - - const _cpp_value_type& value() const - { - return _value; - } - - operator _cpp_value_type() const { return _value; } - - template - void _bind(Target& target, size_t index) const - { - target._bind_floating_point_parameter(index, &_value, _is_null); - } - - private: - _cpp_value_type _value; - bool _is_null; - }; - - template - struct _result_field_t; - - // I am SO waiting for concepts lite! - template - struct field_methods_t - { - static constexpr bool _null_is_trivial = true; - operator _cpp_value_type() const { return static_cast(*this).value(); } - }; - - template - struct field_methods_t< - _result_field_t, - typename std::enable_if::value - and column_spec_can_be_null_t::value - and not null_is_trivial_value_t::value>::type> - { - static constexpr bool _null_is_trivial = false; - }; - - template - struct _result_field_t: public field_methods_t<_result_field_t> - { - using _field_methods_t = field_methods_t<_result_field_t>; - - using _traits = make_traits>; - - struct _recursive_traits - { - using _parameters = std::tuple<>; - using _provided_tables = detail::type_set<>; - using _provided_outer_tables = detail::type_set<>; - using _required_tables = detail::type_set<>; - using _extra_tables = detail::type_set<>; - using _can_be_null = column_spec_can_be_null_t; - }; - - _result_field_t(): - _is_valid(false), - _is_null(true), - _value(0) - {} - - void _validate() - { - _is_valid = true; - } - - void _invalidate() - { - _is_valid = false; - _is_null = true; - _value = 0; - } - - 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) - { - if (enforce_null_result_treatment_t::value and not null_is_trivial_value_t::value) - { - throw exception("accessing value of NULL field"); - } - else - { - return 0; - } - } - return _value; - } - - template - void _bind(Target& target, size_t i) - { - target._bind_floating_point_result(i, &_value, &_is_null); - } - - private: - bool _is_valid; - bool _is_null; - _cpp_value_type _value; - }; - - template - struct _is_valid_operand - { - static constexpr bool value = - is_expression_t::value // expressions are OK - and is_numeric_t::value // the correct value type is required, of course - ; - }; - - template - struct expression_operators: public basic_expression_operators + _parameter_t& operator=(const std::nullptr_t&) { - template - plus_t> operator +(T t) const - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs operand"); + _value = 0; + _is_null = true; + return *this; + } - return { *static_cast(this), rhs{t} }; - } + bool is_null() const + { + return _is_null; + } - template - minus_t> operator -(T t) const - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs operand"); + const _cpp_value_type& value() const + { + return _value; + } - return { *static_cast(this), rhs{t} }; - } + operator _cpp_value_type() const { return _value; } - template - multiplies_t> operator *(T t) const - { - using rhs = wrap_operand_t; - - return { *static_cast(this), rhs{t} }; - } - - template - divides_t> operator /(T t) const - { - using rhs = wrap_operand_t; - - return { *static_cast(this), rhs{t} }; - } - - unary_plus_t operator +() const + template + void _bind(Target& target, size_t index) const { - return { *static_cast(this) }; + target._bind_floating_point_parameter(index, &_value, _is_null); } - unary_minus_t operator -() const - { - return { *static_cast(this) }; - } - }; - - template - struct column_operators - { - template - auto operator +=(T t) const -> assignment_t>> - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); - - return { *static_cast(this), { *static_cast(this), rhs{t} } }; - } - - template - auto operator -=(T t) const -> assignment_t>> - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); - - return { *static_cast(this), { *static_cast(this), rhs{t} } }; - } - - template - auto operator /=(T t) const -> assignment_t>> - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); - - return { *static_cast(this), { *static_cast(this), rhs{t} } }; - } - - template - auto operator *=(T t) const -> assignment_t>> - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); - - return { *static_cast(this), { *static_cast(this), rhs{t} } }; - } - }; + private: + _cpp_value_type _value; + bool _is_null; }; - template - inline std::ostream& operator<<(std::ostream& os, const floating_point::_result_field_t& e) + template + struct _is_valid_operand { - return os << e.value(); - } - } + static constexpr bool value = + is_expression_t::value // expressions are OK + and is_numeric_t::value // the correct value type is required, of course + ; + }; - using floating_point = detail::floating_point; + template + struct expression_operators: public basic_expression_operators + { + template + plus_t> operator +(T t) const + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs operand"); + + return { *static_cast(this), rhs{t} }; + } + + template + minus_t> operator -(T t) const + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs operand"); + + return { *static_cast(this), rhs{t} }; + } + + template + multiplies_t> operator *(T t) const + { + using rhs = wrap_operand_t; + + return { *static_cast(this), rhs{t} }; + } + + template + divides_t> operator /(T t) const + { + using rhs = wrap_operand_t; + + return { *static_cast(this), rhs{t} }; + } + + unary_plus_t operator +() const + { + return { *static_cast(this) }; + } + + unary_minus_t operator -() const + { + return { *static_cast(this) }; + } + }; + + template + struct column_operators + { + template + auto operator +=(T t) const -> assignment_t>> + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); + + return { *static_cast(this), { *static_cast(this), rhs{t} } }; + } + + template + auto operator -=(T t) const -> assignment_t>> + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); + + return { *static_cast(this), { *static_cast(this), rhs{t} } }; + } + + template + auto operator /=(T t) const -> assignment_t>> + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); + + return { *static_cast(this), { *static_cast(this), rhs{t} } }; + } + + template + auto operator *=(T t) const -> assignment_t>> + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); + + return { *static_cast(this), { *static_cast(this), rhs{t} } }; + } + }; + }; + + template + struct result_field_t: public result_field_methods_t> + { + static_assert(std::is_same, floating_point>::value, "field type mismatch"); + using _cpp_value_type = typename floating_point::_cpp_value_type; + + result_field_t(): + _is_valid(false), + _is_null(true), + _value(0) + {} + + void _validate() + { + _is_valid = true; + } + + void _invalidate() + { + _is_valid = false; + _is_null = true; + _value = 0; + } + + bool is_null() const + { + if (not _is_valid) + throw exception("accessing is_null in non-existing row"); + return _is_null; + } + + bool _is_trivial() const + { + if (not _is_valid) + throw exception("accessing is_null in non-existing row"); + + return value() == 0; + } + + _cpp_value_type value() const + { + if (not _is_valid) + throw exception("accessing value in non-existing row"); + + if (_is_null) + { + if (enforce_null_result_treatment_t::value and not null_is_trivial_value_t::value) + { + throw exception("accessing value of NULL field"); + } + else + { + return 0; + } + } + return _value; + } + + template + void _bind(Target& target, size_t i) + { + target._bind_floating_point_result(i, &_value, &_is_null); + } + + private: + bool _is_valid; + bool _is_null; + _cpp_value_type _value; + }; + + template + inline std::ostream& operator<<(std::ostream& os, const result_field_t& e) + { + return serialize(e, os); + } } #endif diff --git a/include/sqlpp11/functions.h b/include/sqlpp11/functions.h index 6c13fd7e..af8ebdb2 100644 --- a/include/sqlpp11/functions.h +++ b/include/sqlpp11/functions.h @@ -42,6 +42,7 @@ #include #include #include // Csaba Csoma suggests: unsafe_sql instead of verbatim +#include namespace sqlpp { @@ -99,6 +100,18 @@ namespace sqlpp return { context.str() }; } + template + auto is_null(Expression e) -> decltype(e.is_null()) + { + return e.is_null(); + } + + template + auto is_not_null(Expression e) -> decltype(e.is_not_null()) + { + return e.is_not_null(); + } + template struct value_list_t // to be used in .in() method { diff --git a/include/sqlpp11/integral.h b/include/sqlpp11/integral.h index 8dad0912..003690ce 100644 --- a/include/sqlpp11/integral.h +++ b/include/sqlpp11/integral.h @@ -33,284 +33,257 @@ #include #include #include +#include namespace sqlpp { - namespace detail + // integral value type + struct integral { + using _traits = make_traits; + using _tag = ::sqlpp::tag::is_integral; + using _cpp_value_type = int64_t; - // integral value type - struct integral + struct _parameter_t { - using _traits = make_traits; - using _tag = ::sqlpp::tag::is_integral; - using _cpp_value_type = int64_t; + using _value_type = integral; - struct _parameter_t + _parameter_t(): + _value(0), + _is_null(true) + {} + + explicit _parameter_t(const _cpp_value_type& value): + _value(value), + _is_null(false) + {} + + _parameter_t& operator=(const _cpp_value_type& value) { - using _value_type = integral; + _value = value; + _is_null = false; + return *this; + } - _parameter_t(): - _value(0), - _is_null(true) - {} - - explicit _parameter_t(const _cpp_value_type& value): - _value(value), - _is_null(false) - {} - - _parameter_t& operator=(const _cpp_value_type& value) - { - _value = value; - _is_null = false; - return *this; - } - - void set_null() - { - _value = 0; - _is_null = true; - } - - bool is_null() const - { - return _is_null; - } - - const _cpp_value_type& value() const - { - return _value; - } - - operator _cpp_value_type() const { return _value; } - - template - void _bind(Target& target, size_t index) const - { - target._bind_integral_parameter(index, &_value, _is_null); - } - - private: - _cpp_value_type _value; - bool _is_null; - }; - - template - struct _result_field_t; - - // I am SO waiting for concepts lite! - template - struct field_methods_t - { - static constexpr bool _null_is_trivial = true; - operator _cpp_value_type() const { return static_cast(*this).value(); } - }; - - template - struct field_methods_t< - _result_field_t, - typename std::enable_if::value - and column_spec_can_be_null_t::value - and not null_is_trivial_value_t::value>::type> - { - static constexpr bool _null_is_trivial = false; - }; - - template - struct _result_field_t: public field_methods_t<_result_field_t> - { - using _field_methods_t = field_methods_t<_result_field_t>; - - using _traits = make_traits>; - - struct _recursive_traits - { - using _parameters = std::tuple<>; - using _provided_tables = detail::type_set<>; - using _provided_outer_tables = detail::type_set<>; - using _required_tables = detail::type_set<>; - using _extra_tables = detail::type_set<>; - using _can_be_null = column_spec_can_be_null_t; - }; - - _result_field_t(): - _is_valid(false), - _is_null(true), - _value(0) - {} - - void _invalidate() - { - _is_valid = false; - _is_null = true; - _value = 0; - } - - void _validate() - { - _is_valid = true; - } - - 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) - { - if (enforce_null_result_treatment_t::value and not null_is_trivial_value_t::value) - { - throw exception("accessing value of NULL field"); - } - else - { - return 0; - } - } - return _value; - } - - template - void _bind(Target& target, size_t i) - { - target._bind_integral_result(i, &_value, &_is_null); - } - - private: - bool _is_valid; - bool _is_null; - _cpp_value_type _value; - }; - - template - struct _is_valid_operand - { - static constexpr bool value = - is_expression_t::value // expressions are OK - and is_numeric_t::value // the correct value type is required, of course - ; - }; - - template - struct expression_operators: public basic_expression_operators + void set_null() { - template - plus_t, wrap_operand_t> operator +(T t) const - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs operand"); + _value = 0; + _is_null = true; + } - return { *static_cast(this), {t} }; - } + bool is_null() const + { + return _is_null; + } - template - minus_t, wrap_operand_t> operator -(T t) const - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs operand"); + const _cpp_value_type& value() const + { + return _value; + } - return { *static_cast(this), {t} }; - } + operator _cpp_value_type() const { return _value; } - template - multiplies_t, wrap_operand_t> operator *(T t) const - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs operand"); - - return { *static_cast(this), {t} }; - } - - template - divides_t> operator /(T t) const - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs operand"); - - return { *static_cast(this), {t} }; - } - - template - modulus_t> operator %(T t) const - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs operand"); - - return { *static_cast(this), {t} }; - } - - unary_plus_t operator +() const + template + void _bind(Target& target, size_t index) const { - return { *static_cast(this) }; + target._bind_integral_parameter(index, &_value, _is_null); } - unary_minus_t operator -() const - { - return { *static_cast(this) }; - } - }; - - template - struct column_operators - { - template - auto operator +=(T t) const -> assignment_t, wrap_operand_t>> - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); - - return { *static_cast(this), { *static_cast(this), rhs{t} } }; - } - - template - auto operator -=(T t) const -> assignment_t, wrap_operand_t>> - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); - - return { *static_cast(this), { *static_cast(this), rhs{t} } }; - } - - template - auto operator /=(T t) const -> assignment_t>> - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); - - return { *static_cast(this), { *static_cast(this), rhs{t} } }; - } - - template - auto operator *=(T t) const -> assignment_t, wrap_operand_t>> - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); - - return { *static_cast(this), { *static_cast(this), rhs{t} } }; - } - }; + private: + _cpp_value_type _value; + bool _is_null; }; - template - inline std::ostream& operator<<(std::ostream& os, const integral::_result_field_t& e) + template + struct _is_valid_operand { - return os << e.value(); - } - } + static constexpr bool value = + is_expression_t::value // expressions are OK + and is_numeric_t::value // the correct value type is required, of course + ; + }; - using tinyint = detail::integral; - using smallint = detail::integral; - using integer = detail::integral; - using bigint = detail::integral; + template + struct expression_operators: public basic_expression_operators + { + template + plus_t, wrap_operand_t> operator +(T t) const + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs operand"); + + return { *static_cast(this), {t} }; + } + + template + minus_t, wrap_operand_t> operator -(T t) const + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs operand"); + + return { *static_cast(this), {t} }; + } + + template + multiplies_t, wrap_operand_t> operator *(T t) const + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs operand"); + + return { *static_cast(this), {t} }; + } + + template + divides_t> operator /(T t) const + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs operand"); + + return { *static_cast(this), {t} }; + } + + template + modulus_t> operator %(T t) const + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs operand"); + + return { *static_cast(this), {t} }; + } + + unary_plus_t operator +() const + { + return { *static_cast(this) }; + } + + unary_minus_t operator -() const + { + return { *static_cast(this) }; + } + }; + + template + struct column_operators + { + template + auto operator +=(T t) const -> assignment_t, wrap_operand_t>> + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); + + return { *static_cast(this), { *static_cast(this), rhs{t} } }; + } + + template + auto operator -=(T t) const -> assignment_t, wrap_operand_t>> + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); + + return { *static_cast(this), { *static_cast(this), rhs{t} } }; + } + + template + auto operator /=(T t) const -> assignment_t>> + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); + + return { *static_cast(this), { *static_cast(this), rhs{t} } }; + } + + template + auto operator *=(T t) const -> assignment_t, wrap_operand_t>> + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); + + return { *static_cast(this), { *static_cast(this), rhs{t} } }; + } + }; + }; + + + template + struct result_field_t: public result_field_methods_t> + { + static_assert(std::is_same, integral>::value, "field type mismatch"); + using _cpp_value_type = typename integral::_cpp_value_type; + + result_field_t(): + _is_valid(false), + _is_null(true), + _value(0) + {} + + void _invalidate() + { + _is_valid = false; + _is_null = true; + _value = 0; + } + + void _validate() + { + _is_valid = true; + } + + bool is_null() const + { + if (not _is_valid) + throw exception("accessing is_null in non-existing row"); + return _is_null; + } + + bool _is_trivial() const + { + if (not _is_valid) + throw exception("accessing is_null in non-existing row"); + + return value() == 0; + } + + _cpp_value_type value() const + { + if (not _is_valid) + throw exception("accessing value in non-existing row"); + + if (_is_null) + { + if (enforce_null_result_treatment_t::value and not null_is_trivial_value_t::value) + { + throw exception("accessing value of NULL field"); + } + else + { + return 0; + } + } + return _value; + } + + template + void _bind(Target& target, size_t i) + { + target._bind_integral_result(i, &_value, &_is_null); + } + + private: + bool _is_valid; + bool _is_null; + _cpp_value_type _value; + }; + + template + inline std::ostream& operator<<(std::ostream& os, const result_field_t& e) + { + return serialize(e, os); + } + + using tinyint = integral; + using smallint = integral; + using integer = integral; + using bigint = integral; } #endif diff --git a/include/sqlpp11/interpreter.h b/include/sqlpp11/interpreter.h index cd7c9690..17ea9ffa 100644 --- a/include/sqlpp11/interpreter.h +++ b/include/sqlpp11/interpreter.h @@ -36,7 +36,7 @@ namespace sqlpp { static void _(const T& t, Context& context) { - static_assert(wrong_t::value, "missing interpreter specialization"); + static_assert(wrong_t::value, "missing interpreter specialization"); } }; diff --git a/include/sqlpp11/into.h b/include/sqlpp11/into.h index 3d2b2f90..6cf97ff4 100644 --- a/include/sqlpp11/into.h +++ b/include/sqlpp11/into.h @@ -140,10 +140,10 @@ namespace sqlpp template using _new_statement_t = typename Policies::template _new_statement_t; - static void _check_consistency() - { - static_assert(wrong_t::value, "into() required"); - } + static void _check_consistency() + { + static_assert(wrong_t<_methods_t>::value, "into() required"); + } template auto into(Args... args) diff --git a/include/sqlpp11/join.h b/include/sqlpp11/join.h index d5ae7b4c..6e90ca37 100644 --- a/include/sqlpp11/join.h +++ b/include/sqlpp11/join.h @@ -50,14 +50,14 @@ namespace sqlpp struct left_outer_join_t { template - using _provided_outer_tables = detail::make_joined_set_t, provided_tables_of>; + using _provided_outer_tables = detail::make_joined_set_t, provided_outer_tables_of>; static constexpr const char* _name = " LEFT OUTER "; }; struct right_outer_join_t { template - using _provided_outer_tables = detail::make_joined_set_t, provided_outer_tables_of>; + using _provided_outer_tables = detail::make_joined_set_t, provided_tables_of>; static constexpr const char* _name = " RIGHT OUTER "; }; diff --git a/include/sqlpp11/multi_column.h b/include/sqlpp11/multi_column.h index 892a8c4d..5b7763a9 100644 --- a/include/sqlpp11/multi_column.h +++ b/include/sqlpp11/multi_column.h @@ -117,7 +117,7 @@ namespace sqlpp static void _(const T& t, Context& context) { - static_assert(wrong_t::value, "multi_column must be used with an alias"); + static_assert(wrong_t::value, "multi_column must be used with an alias"); } }; diff --git a/include/sqlpp11/null.h b/include/sqlpp11/null.h index 63586c7f..93e0706b 100644 --- a/include/sqlpp11/null.h +++ b/include/sqlpp11/null.h @@ -33,7 +33,7 @@ namespace sqlpp { struct null_t { - using _traits = make_traits; + using _traits = make_traits; using _recursive_traits = make_recursive_traits<>; }; diff --git a/include/sqlpp11/parameter.h b/include/sqlpp11/parameter.h index 025ed94e..665523f9 100644 --- a/include/sqlpp11/parameter.h +++ b/include/sqlpp11/parameter.h @@ -83,7 +83,7 @@ namespace sqlpp auto parameter(const ValueType&, const AliasProvider&) -> parameter_t, AliasProvider> { - static_assert(is_expression_t::value, "first argument is not a value type"); + static_assert(is_value_type_t::value, "first argument is not a value type"); static_assert(is_alias_provider_t::value, "second argument is not an alias provider"); return {}; } diff --git a/include/sqlpp11/parameter_list.h b/include/sqlpp11/parameter_list.h index c023e3f7..89d75d33 100644 --- a/include/sqlpp11/parameter_list.h +++ b/include/sqlpp11/parameter_list.h @@ -37,7 +37,7 @@ namespace sqlpp template struct parameter_list_t { - static_assert(wrong_t::value, "Template parameter for parameter_list_t has to be a tuple"); + static_assert(wrong_t::value, "Template parameter for parameter_list_t has to be a tuple"); }; template diff --git a/include/sqlpp11/result_field.h b/include/sqlpp11/result_field.h new file mode 100644 index 00000000..e78b3938 --- /dev/null +++ b/include/sqlpp11/result_field.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013-2014, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_RESULT_FIELD_H +#define SQLPP_RESULT_FIELD_H + +#include +#include + +namespace sqlpp +{ + template + struct result_field_t + { + static_assert(wrong_t::value, "Missing specialization for result_field_t"); + }; + + template + struct serializer_t> + { + using T = result_field_t; + + static Context& _(const T& t, Context& context) + { + if (t.is_null() and not null_is_trivial_value_t::value) + { + context << "NULL"; + } + else + { + context << t.value(); + } + return context; + } + }; + + +} +#endif diff --git a/include/sqlpp11/result_field_methods.h b/include/sqlpp11/result_field_methods.h new file mode 100644 index 00000000..54492a38 --- /dev/null +++ b/include/sqlpp11/result_field_methods.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2013-2014, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_RESULT_FIELD_METHODS_H +#define SQLPP_RESULT_FIELD_METHODS_H + +#include +#include + +namespace sqlpp +{ + namespace detail + { + template + struct get_field_spec_impl + { + static_assert(wrong_t::value, "Invalid argument for get_field_spec"); + }; + + template class Field, typename ValueType, typename Db, typename FieldSpec> + struct get_field_spec_impl> + { + using type = FieldSpec; + }; + + template + using get_field_spec_t = typename get_field_spec_impl::type; + } + + template + struct result_field_methods_base_t + { + using _field_spec_t = detail::get_field_spec_t; + static constexpr bool _null_is_trivial = true; + operator cpp_value_type_of<_field_spec_t>() const { return static_cast(*this).value(); } + }; + + template class Field, typename ValueType, typename Db, typename FieldSpec> + struct result_field_methods_base_t< + Field, + typename std::enable_if::value + and column_spec_can_be_null_t::value + and not null_is_trivial_value_t::value>::type> + { + using _field_spec_t = FieldSpec; + static constexpr bool _null_is_trivial = false; + }; + + template + struct result_field_methods_t: public result_field_methods_base_t, + public alias_operators + { + using _base_t = result_field_methods_base_t; + using _field_spec_t = typename _base_t::_field_spec_t; + + using _traits = make_traits, + tag::is_result_field, + tag::is_expression, + tag_if>; + + struct _recursive_traits + { + using _parameters = std::tuple<>; + using _provided_tables = detail::type_set<>; + using _provided_outer_tables = detail::type_set<>; + using _required_tables = detail::type_set<>; + using _extra_tables = detail::type_set<>; + using _can_be_null = column_spec_can_be_null_t<_field_spec_t>; + }; + + }; + +} +#endif diff --git a/include/sqlpp11/result_row.h b/include/sqlpp11/result_row.h index fe2a6c63..676a8eef 100644 --- a/include/sqlpp11/result_row.h +++ b/include/sqlpp11/result_row.h @@ -41,9 +41,9 @@ namespace sqlpp template struct result_field: - public FieldSpec::_name_t::template _member_t::template _result_field_t> + public FieldSpec::_name_t::template _member_t, Db, FieldSpec>> { - using _field = typename FieldSpec::_name_t::template _member_t::template _result_field_t>; + using _field = typename FieldSpec::_name_t::template _member_t, Db, FieldSpec>>; result_field() = default; @@ -184,12 +184,12 @@ namespace sqlpp using _impl = detail::result_row_impl, FieldSpecs...>; struct _field_spec_t { - using _traits = make_traits; + using _traits = make_traits; using _recursive_traits = make_recursive_traits<>; struct _name_t {}; }; - using _field_type = detail::text::_result_field_t; + using _field_type = result_field_t; static constexpr size_t _last_static_index = _impl::_last_index; bool _is_valid; diff --git a/include/sqlpp11/rhs_is_null.h b/include/sqlpp11/rhs_is_null.h index 1eda20d9..aa6d8ed0 100644 --- a/include/sqlpp11/rhs_is_null.h +++ b/include/sqlpp11/rhs_is_null.h @@ -32,7 +32,7 @@ namespace sqlpp { template - struct is_trivial_t + struct rhs_is_null_t { static constexpr bool _(const T&) { @@ -41,27 +41,29 @@ namespace sqlpp }; template - struct is_trivial_t::value, void>::type> + struct rhs_is_null_t::value, void>::type> { static bool _(const T& t) { - return t._is_trivial(); + return t._is_null(); } }; template - bool is_trivial(const T& t) + struct rhs_is_null_t::value, void>::type> { - return is_trivial_t::_(t); - } + static bool _(const T& t) + { + return t.is_null(); + } + }; template constexpr bool rhs_is_null(const Expression& e) { - return (((trivial_value_is_null_t::value or is_tvin_t::value) - and is_trivial(e._rhs)) - or (std::is_same::value)); + return rhs_is_null_t::type::_rhs_t>::_(e._rhs); } + } #endif diff --git a/include/sqlpp11/rhs_is_trivial.h b/include/sqlpp11/rhs_is_trivial.h new file mode 100644 index 00000000..5a38db17 --- /dev/null +++ b/include/sqlpp11/rhs_is_trivial.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2013-2014, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_RHS_IS_TRIVIAL_H +#define SQLPP_RHS_IS_TRIVIAL_H + +#include + +namespace sqlpp +{ + template + struct rhs_is_trivial_t + { + static constexpr bool _(const T&) + { + return false; + } + }; + + template + struct rhs_is_trivial_t::value, void>::type> + { + static bool _(const T& t) + { + return t._is_trivial(); + } + }; + + template + struct rhs_is_trivial_t::value, void>::type> + { + static bool _(const T& t) + { + return t._is_trivial(); + } + }; + + template + struct rhs_is_trivial_t::value, void>::type> + { + static bool _(const T& t) + { + if (null_is_trivial_value_t::value) + { + return t._is_trivial(); + } + else + { + if (t.is_null()) + { + return false; + } + else + { + return t._is_trivial(); + } + } + } + }; + + template + constexpr bool rhs_is_trivial(const Expression& e) + { + return rhs_is_trivial_t::type::_rhs_t>::_(e._rhs); + } +} + +#endif diff --git a/include/sqlpp11/serializer.h b/include/sqlpp11/serializer.h index 682c9602..04629bda 100644 --- a/include/sqlpp11/serializer.h +++ b/include/sqlpp11/serializer.h @@ -36,7 +36,7 @@ namespace sqlpp { static void _(const T& t, Context& context) { - static_assert(wrong_t::value, "missing serializer specialization"); + static_assert(wrong_t::value, "missing serializer specialization"); } }; diff --git a/include/sqlpp11/sqlpp11.h b/include/sqlpp11/sqlpp11.h index e0ccca48..d4278a2f 100644 --- a/include/sqlpp11/sqlpp11.h +++ b/include/sqlpp11/sqlpp11.h @@ -35,6 +35,7 @@ #include #include #include +#include #endif diff --git a/include/sqlpp11/statement.h b/include/sqlpp11/statement.h index 64d17e82..615a50cb 100644 --- a/include/sqlpp11/statement.h +++ b/include/sqlpp11/statement.h @@ -112,7 +112,7 @@ namespace sqlpp ::sqlpp::detail::make_intersect_set_t< required_tables_of<_result_type_provider>, provided_outer_tables_of - >::size::value>; + >::size::value != 0>; }; }; } diff --git a/include/sqlpp11/text.h b/include/sqlpp11/text.h index e90d5e6c..58affcfc 100644 --- a/include/sqlpp11/text.h +++ b/include/sqlpp11/text.h @@ -32,223 +32,222 @@ #include #include #include +#include namespace sqlpp { - namespace detail + // text value type + struct text { - // text value type - struct text + using _traits = make_traits; + using _tag = ::sqlpp::tag::is_text; + using _cpp_value_type = std::string; + + struct _parameter_t { - using _traits = make_traits; - using _tag = ::sqlpp::tag::is_text; - using _cpp_value_type = std::string; + using _value_type = text; - struct _parameter_t + _parameter_t(): + _value(""), + _is_null(true) + {} + + _parameter_t(const _cpp_value_type& value): + _value(value), + _is_null(false) + {} + + _parameter_t& operator=(const _cpp_value_type& value) { - using _value_type = integral; + _value = value; + _is_null = false; + return *this; + } - _parameter_t(): - _value(""), - _is_null(true) - {} - - _parameter_t(const _cpp_value_type& value): - _value(value), - _is_null(false) - {} - - _parameter_t& operator=(const _cpp_value_type& value) - { - _value = value; - _is_null = false; - return *this; - } - - _parameter_t& operator=(const std::nullptr_t&) - { - _value = ""; - _is_null = true; - return *this; - } - - bool is_null() const - { - return _is_null; - } - - _cpp_value_type value() const - { - return _value; - } - - operator _cpp_value_type() const { return value(); } - - template - void _bind(Target& target, size_t index) const - { - target._bind_text_parameter(index, &_value, _is_null); - } - - private: - _cpp_value_type _value; - bool _is_null; - }; - - template - struct _result_field_t; - - // I am SO waiting for concepts lite! - template - struct field_methods_t - { - static constexpr bool _null_is_trivial = true; - operator _cpp_value_type() const { return static_cast(*this).value(); } - }; - - template - struct field_methods_t< - _result_field_t, - typename std::enable_if::value - and column_spec_can_be_null_t::value - and not null_is_trivial_value_t::value>::type> - { - static constexpr bool _null_is_trivial = false; - }; - - template - struct _result_field_t: public field_methods_t<_result_field_t> - { - using _field_methods_t = field_methods_t<_result_field_t>; - - using _traits = make_traits>; - - struct _recursive_traits - { - using _parameters = std::tuple<>; - using _provided_tables = detail::type_set<>; - using _provided_outer_tables = detail::type_set<>; - using _required_tables = detail::type_set<>; - using _extra_tables = detail::type_set<>; - using _can_be_null = column_spec_can_be_null_t; - }; - - _result_field_t(): - _is_valid(false), - _value_ptr(nullptr), - _len(0) - {} - - void _validate() - { - _is_valid = true; - } - - void _invalidate() - { - _is_valid = false; - _value_ptr = nullptr; - _len = 0; - } - - bool operator==(const _cpp_value_type& rhs) const { return value() == rhs; } - bool operator!=(const _cpp_value_type& rhs) const { return not operator==(rhs); } - - bool is_null() const - { - if (not _is_valid) - throw exception("accessing is_null in non-existing row"); - return _value_ptr == nullptr; - } - - _cpp_value_type value() const - { - if (not _is_valid) - throw exception("accessing value in non-existing row"); - - if (not _value_ptr) - { - if (enforce_null_result_treatment_t::value and not null_is_trivial_value_t::value) - { - throw exception("accessing value of NULL field"); - } - else - { - return ""; - } - } - return std::string(_value_ptr, _value_ptr + _len); - } - - template - void _bind(Target& target, size_t i) - { - target._bind_text_result(i, &_value_ptr, &_len); - } - - private: - bool _is_valid; - const char* _value_ptr; - size_t _len; - }; - - template - struct _is_valid_operand - { - static constexpr bool value = - is_expression_t::value // expressions are OK - and is_text_t::value // the correct value type is required, of course - ; - }; - - template - struct expression_operators: public basic_expression_operators + _parameter_t& operator=(const std::nullptr_t&) { - template - concat_t> operator+(T t) const - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs operand"); + _value = ""; + _is_null = true; + return *this; + } - return { *static_cast(this), {t} }; - } + bool is_null() const + { + return _is_null; + } - template - like_t> like(T t) const - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid argument for like()"); + _cpp_value_type value() const + { + return _value; + } - return { *static_cast(this), {t} }; - } - }; + operator _cpp_value_type() const { return value(); } - template - struct column_operators + template + void _bind(Target& target, size_t index) const { - template - auto operator +=(T t) const -> assignment_t>> - { - using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); + target._bind_text_parameter(index, &_value, _is_null); + } - return { *static_cast(this), { *static_cast(this), rhs{t} } }; - } - }; + private: + _cpp_value_type _value; + bool _is_null; }; - template - inline std::ostream& operator<<(std::ostream& os, const text::_result_field_t& e) + + template + struct _is_valid_operand + { + static constexpr bool value = + is_expression_t::value // expressions are OK + and is_text_t::value // the correct value type is required, of course + ; + }; + + template + struct expression_operators: public basic_expression_operators + { + template + concat_t> operator+(T t) const + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs operand"); + + return { *static_cast(this), {t} }; + } + + template + like_t> like(T t) const + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid argument for like()"); + + return { *static_cast(this), {t} }; + } + }; + + template + struct column_operators + { + template + auto operator +=(T t) const -> assignment_t>> + { + using rhs = wrap_operand_t; + static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); + + return { *static_cast(this), { *static_cast(this), rhs{t} } }; + } + }; + }; + + template + struct result_field_t: public result_field_methods_t> + { + static_assert(std::is_same, text>::value, "field type mismatch"); + using _cpp_value_type = typename text::_cpp_value_type; + + result_field_t(): + _is_valid(false), + _value_ptr(nullptr), + _len(0) + {} + + void _validate() + { + _is_valid = true; + } + + void _invalidate() + { + _is_valid = false; + _value_ptr = nullptr; + _len = 0; + } + + bool operator==(const _cpp_value_type& rhs) const { return value() == rhs; } + bool operator!=(const _cpp_value_type& rhs) const { return not operator==(rhs); } + + bool is_null() const + { + if (not _is_valid) + throw exception("accessing is_null in non-existing row"); + return _value_ptr == nullptr; + } + + bool _is_trivial() const + { + if (not _is_valid) + throw exception("accessing is_null in non-existing row"); + + return value() == ""; + } + + _cpp_value_type value() const + { + if (not _is_valid) + throw exception("accessing value in non-existing row"); + + if (not _value_ptr) + { + if (enforce_null_result_treatment_t::value and not null_is_trivial_value_t::value) + { + throw exception("accessing value of NULL field"); + } + else + { + return ""; + } + } + return std::string(_value_ptr, _value_ptr + _len); + } + + template + void _bind(Target& target, size_t i) + { + target._bind_text_result(i, &_value_ptr, &_len); + } + + private: + bool _is_valid; + const char* _value_ptr; + size_t _len; + }; + + template + struct serializer_t> + { + using T = result_field_t; + + static Context& _(const T& t, Context& context) + { + if (t.is_null() and not null_is_trivial_value_t::value) + { + context << "NULL"; + } + else + { + context << '\'' << context.escape(t.value()) << '\''; + } + return context; + } + }; + + template + inline std::ostream& operator<<(std::ostream& os, const result_field_t& e) + { + if (e.is_null() and not null_is_trivial_value_t::value) + { + return os << "NULL"; + } + else { return os << e.value(); } - } + } - using text = detail::text; - using blob = detail::text; - using varchar = detail::text; - using char_ = detail::text; + using blob = text; + using varchar = text; + using char_ = text; } #endif diff --git a/include/sqlpp11/tvin.h b/include/sqlpp11/tvin.h index 0b80cc44..ad65ed38 100644 --- a/include/sqlpp11/tvin.h +++ b/include/sqlpp11/tvin.h @@ -63,7 +63,7 @@ namespace sqlpp static Context& _(const T& t, Context& context) { - static_assert(wrong_t::value, "tvin may only be used with operators =, == and !="); + static_assert(wrong_t::value, "tvin may only be used with operators =, == and !="); } }; @@ -108,6 +108,11 @@ namespace sqlpp return _value._is_trivial(); } + bool _is_null() const + { + return _value._is_trivial(); + } + _operand_t _value; }; @@ -150,8 +155,7 @@ namespace sqlpp auto tvin(Operand operand) -> tvin_arg_t::type> { using _operand_t = typename wrap_operand::type; - static_assert(std::is_same<_operand_t, text_operand>::value - or not std::is_same<_operand_t, Operand>::value, "tvin() used with invalid type (only string and primitive types allowed)"); + static_assert(not std::is_same<_operand_t, Operand>::value or is_result_field_t::value, "tvin() used with invalid type (only string and primitive types allowed)"); return {{operand}}; } diff --git a/include/sqlpp11/type_traits.h b/include/sqlpp11/type_traits.h index 659d7bab..9b30d438 100644 --- a/include/sqlpp11/type_traits.h +++ b/include/sqlpp11/type_traits.h @@ -71,6 +71,8 @@ namespace sqlpp template\ using name##_t = typename detail::name##_impl::type; + SQLPP_VALUE_TRAIT_GENERATOR(is_value_type); + SQLPP_VALUE_TRAIT_GENERATOR(is_sql_null); SQLPP_VALUE_TRAIT_GENERATOR(is_boolean); SQLPP_VALUE_TRAIT_GENERATOR(is_integral); SQLPP_VALUE_TRAIT_GENERATOR(is_floating_point); @@ -85,6 +87,7 @@ namespace sqlpp SQLPP_VALUE_TRAIT_GENERATOR(is_multi_expression); SQLPP_VALUE_TRAIT_GENERATOR(is_alias); SQLPP_VALUE_TRAIT_GENERATOR(is_select_flag); + SQLPP_VALUE_TRAIT_GENERATOR(is_result_field); SQLPP_VALUE_TRAIT_GENERATOR(must_not_insert); SQLPP_VALUE_TRAIT_GENERATOR(must_not_update); @@ -191,6 +194,9 @@ namespace sqlpp template using value_type_of = typename detail::value_type_of_impl::type; + template + using cpp_value_type_of = typename value_type_of::_cpp_value_type; + template using required_tables_of = typename detail::required_table_of_impl::type; diff --git a/include/sqlpp11/value_or_null.h b/include/sqlpp11/value_or_null.h new file mode 100644 index 00000000..68ce8c83 --- /dev/null +++ b/include/sqlpp11/value_or_null.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2013-2014, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP_VALUE_OR_NULL_H +#define SQLPP_VALUE_OR_NULL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // Csaba Csoma suggests: unsafe_sql instead of verbatim +#include + +namespace sqlpp +{ + template + struct value_or_null_t + { + using _cpp_value_type = typename ValueType::_cpp_value_type; + + using _traits = make_traits; + using _recursive_traits = make_recursive_traits<>; + + value_or_null_t(_cpp_value_type value): + _value(value), + _is_null(false) + {} + + value_or_null_t(const null_t&): + _value(), + _is_null(true) + {} + + typename ValueType::_cpp_value_type _value; + bool _is_null; + }; + + template + auto value_or_null(T t) -> value_or_null_t>> + { + static_assert(is_wrapped_value_t>::value, "value_or_null() is to be called with non-sql-type like int, or string or null"); + return { t }; + } + + template + auto value_or_null(null_t t) -> value_or_null_t + { + static_assert(is_value_type_t::value, "value_or_null() is to be called with non-sql-type like int, or string"); + return { t }; + } + +} + +#endif diff --git a/include/sqlpp11/where.h b/include/sqlpp11/where.h index 20933fee..cd2b924b 100644 --- a/include/sqlpp11/where.h +++ b/include/sqlpp11/where.h @@ -224,7 +224,7 @@ namespace sqlpp static void _check_consistency() { - static_assert(Required ? wrong_t::value : true, "where expression required, e.g. where(true)"); + static_assert(Required ? wrong_t<_methods_t>::value : true, "where expression required, e.g. where(true)"); } template diff --git a/include/sqlpp11/wrap_operand.h b/include/sqlpp11/wrap_operand.h index 0b0f8d23..ffab6e94 100644 --- a/include/sqlpp11/wrap_operand.h +++ b/include/sqlpp11/wrap_operand.h @@ -33,17 +33,14 @@ namespace sqlpp { - namespace detail - { - struct boolean; - struct integral; - struct floating_point; - struct text; - } + struct boolean; + struct integral; + struct floating_point; + struct text; struct boolean_operand { - using _traits = make_traits<::sqlpp::detail::boolean, ::sqlpp::tag::is_expression, ::sqlpp::tag::is_wrapped_value>; + using _traits = make_traits<::sqlpp::boolean, ::sqlpp::tag::is_expression, ::sqlpp::tag::is_wrapped_value>; using _recursive_traits = make_recursive_traits<>; using _value_t = bool; @@ -81,7 +78,7 @@ namespace sqlpp struct integral_operand { - using _traits = make_traits<::sqlpp::detail::integral, ::sqlpp::tag::is_expression, ::sqlpp::tag::is_wrapped_value>; + using _traits = make_traits<::sqlpp::integral, ::sqlpp::tag::is_expression, ::sqlpp::tag::is_wrapped_value>; using _recursive_traits = make_recursive_traits<>; using _value_t = int64_t; @@ -120,7 +117,7 @@ namespace sqlpp struct floating_point_operand { - using _traits = make_traits<::sqlpp::detail::floating_point, ::sqlpp::tag::is_expression, ::sqlpp::tag::is_wrapped_value>; + using _traits = make_traits<::sqlpp::floating_point, ::sqlpp::tag::is_expression, ::sqlpp::tag::is_wrapped_value>; using _recursive_traits = make_recursive_traits<>; using _value_t = double; @@ -158,7 +155,7 @@ namespace sqlpp struct text_operand { - using _traits = make_traits<::sqlpp::detail::text, ::sqlpp::tag::is_expression, ::sqlpp::tag::is_wrapped_value>; + using _traits = make_traits<::sqlpp::text, ::sqlpp::tag::is_expression, ::sqlpp::tag::is_wrapped_value>; using _recursive_traits = make_recursive_traits<>; using _value_t = std::string; @@ -219,7 +216,7 @@ namespace sqlpp }; template - struct wrap_operand::value>::type> + struct wrap_operand::value and not is_result_field_t::value>::type> { using type = text_operand; }; diff --git a/test_constraints/CMakeLists.txt b/test_constraints/CMakeLists.txt new file mode 100644 index 00000000..678412dc --- /dev/null +++ b/test_constraints/CMakeLists.txt @@ -0,0 +1,17 @@ +include_directories(${CMAKE_BINARY_DIR}/tests) + +add_executable( + no_conversion_operator_if_null_not_trivial + EXCLUDE_FROM_ALL + no_conversion_operator_if_null_not_trivial.cpp + ) + +add_custom_command(OUTPUT no_conversion_operator_if_null_not_trivial.out + COMMAND ${CMAKE_MAKE_PROGRAM} no_conversion_operator_if_null_not_trivial > ${CMAKE_CURRENT_BINARY_DIR}/no_conversion_operator_if_null_not_trivial.out 2>&1 || true + COMMAND grep "int i = row.alpha" ${CMAKE_CURRENT_BINARY_DIR}/no_conversion_operator_if_null_not_trivial.out > /dev/null + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/no_conversion_operator_if_null_not_trivial.cpp + COMMENT "no_conversion_operator_if_null_not_trivial" + ) + +add_custom_target(test_constraints DEPENDS no_conversion_operator_if_null_not_trivial.out COMMAND true) + diff --git a/test_constraints/no_conversion_operator_if_null_not_trivial.cpp b/test_constraints/no_conversion_operator_if_null_not_trivial.cpp new file mode 100644 index 00000000..7b995626 --- /dev/null +++ b/test_constraints/no_conversion_operator_if_null_not_trivial.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2013-2014, 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 "Sample.h" +#include "MockDb.h" +#include + +EnforceDb edb {}; + +int main() +{ + test::TabBar t; + + static_assert(sqlpp::can_be_null_t::value, "t.alpha can be null"); + static_assert(not sqlpp::null_is_trivial_value_t::value, "t.alpha does not say null_is_trivial"); + + for (const auto& row : edb(select(all_of(t)).from(t).where(true))) + { + static_assert(sqlpp::can_be_null_t::value, "row.alpha can be null"); + static_assert(not sqlpp::null_is_trivial_value_t::value, "row.alpha does not interpret null_is_trivial"); + + int i = row.alpha; + } + + return 0; +} diff --git a/tests/BooleanExpressionTest.cpp b/tests/BooleanExpressionTest.cpp new file mode 100644 index 00000000..3eaf8544 --- /dev/null +++ b/tests/BooleanExpressionTest.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013-2014, 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 "Sample.h" +#include "MockDb.h" +#include + +MockDb db = {}; + +int main() +{ + test::TabBar t; + + auto x = boolean_expression(db, t.alpha == 7); + x = boolean_expression(db, t.gamma); + x = sqlpp::boolean_expression(t.beta.like("%cheesecake")); + + return 0; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ed2c041d..6582425b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,6 +6,7 @@ macro (build_and_run arg) add_test(${arg} ${arg}) endmacro () +build_and_run(BooleanExpressionTest) build_and_run(InterpretTest) build_and_run(InsertTest) build_and_run(RemoveTest) diff --git a/tests/FunctionTest.cpp b/tests/FunctionTest.cpp index e668a668..374a7c20 100644 --- a/tests/FunctionTest.cpp +++ b/tests/FunctionTest.cpp @@ -135,6 +135,12 @@ int main() using TI = decltype(t.alpha.is_null()); using TF = decltype(f.omega.is_null()); using TT = decltype(t.beta.is_null()); + using TTI = decltype(is_null(t.alpha)); + using TTF = decltype(is_null(f.omega)); + using TTT = decltype(is_null(t.beta)); + static_assert(std::is_same::value, "type requirement"); + static_assert(std::is_same::value, "type requirement"); + static_assert(std::is_same::value, "type requirement"); static_assert(sqlpp::is_named_expression_t::value, "type requirement"); static_assert(sqlpp::is_boolean_t::value, "type requirement"); static_assert(not sqlpp::is_numeric_t::value, "type requirement"); @@ -154,6 +160,12 @@ int main() using TI = decltype(t.alpha.is_not_null()); using TF = decltype(f.omega.is_not_null()); using TT = decltype(t.beta.is_not_null()); + using TTI = decltype(is_not_null(t.alpha)); + using TTF = decltype(is_not_null(f.omega)); + using TTT = decltype(is_not_null(t.beta)); + static_assert(std::is_same::value, "type requirement"); + static_assert(std::is_same::value, "type requirement"); + static_assert(std::is_same::value, "type requirement"); static_assert(sqlpp::is_named_expression_t::value, "type requirement"); static_assert(sqlpp::is_boolean_t::value, "type requirement"); static_assert(not sqlpp::is_numeric_t::value, "type requirement"); @@ -356,6 +368,30 @@ int main() static_assert(sqlpp::is_text_t::value, "type requirement"); } + // test value_or_null + { + using TB = decltype(sqlpp::value_or_null(true)); + using TI = decltype(sqlpp::value_or_null(7)); + using TF = decltype(sqlpp::value_or_null(5.6)); + using TT = decltype(sqlpp::value_or_null("hallo")); + using TBN = decltype(sqlpp::value_or_null(sqlpp::null)); + using TIN = decltype(sqlpp::value_or_null(sqlpp::null)); + using TFN = decltype(sqlpp::value_or_null(sqlpp::null)); + using TTN = decltype(sqlpp::value_or_null(sqlpp::null)); + static_assert(std::is_same::value, "type_requirement"); + static_assert(std::is_same::value, "type_requirement"); + static_assert(std::is_same::value, "type_requirement"); + static_assert(std::is_same::value, "type_requirement"); + static_assert(not sqlpp::is_named_expression_t::value, "type requirement"); + static_assert(sqlpp::is_boolean_t::value, "type requirement"); + static_assert(not sqlpp::is_named_expression_t::value, "type requirement"); + static_assert(sqlpp::is_integral_t::value, "type requirement"); + static_assert(not sqlpp::is_named_expression_t::value, "type requirement"); + static_assert(sqlpp::is_floating_point_t::value, "type requirement"); + static_assert(not sqlpp::is_named_expression_t::value, "type requirement"); + static_assert(sqlpp::is_text_t::value, "type requirement"); + } + // test verbatim { using TB = decltype(sqlpp::verbatim("1")); @@ -389,5 +425,20 @@ int main() static_assert(sqlpp::is_alias_t::value, "type requirement"); } + // test tvin + { + static_assert(std::is_same>::value, "integral values are accepted and wrapped") ; + static_assert(std::is_same>::value, "bool values are accepted and wrapped") ; + static_assert(std::is_same>::value, "float values are accepted and wrapped") ; + static_assert(std::is_same>::value, "text values are accepted and wrapped") ; + + for (const auto& row : db(select(all_of(t)).from(t).where(true))) + { + static_assert(std::is_same>::value, "result fields are accepted and not wrapped") ; + static_assert(std::is_same>::value, "result fields are accepted and not wrapped") ; + static_assert(std::is_same>::value, "result fields are accepted and not wrapped") ; + } + } + return 0; } diff --git a/tests/InterpretTest.cpp b/tests/InterpretTest.cpp index 02f00dd5..b927e239 100644 --- a/tests/InterpretTest.cpp +++ b/tests/InterpretTest.cpp @@ -101,7 +101,7 @@ int main() // functions serialize(sqlpp::value(7), printer).str(); - serialize(sqlpp::verbatim("irgendwas integrales"), printer).str(); + serialize(sqlpp::verbatim("irgendwas integrales"), printer).str(); serialize(sqlpp::value_list(std::vector({1,2,3,4,5,6,8})), printer).str(); serialize(exists(select(t.alpha).from(t)), printer).str(); serialize(any(select(t.alpha).from(t)), printer).str(); @@ -156,6 +156,7 @@ int main() serialize(s, printer).str(); } + // distinct aggregate serialize(count(sqlpp::distinct, t.alpha % 7), printer).str(); serialize(avg(sqlpp::distinct, t.alpha - 7), printer).str(); @@ -164,5 +165,12 @@ int main() serialize(select(all_of(t)).from(t).where(true), printer).str(); serialize(select(all_of(t)).from(t).where(false), printer).str(); + for (const auto& row : db(select(all_of(t)).from(t).where(true))) + { + serialize(row.alpha, printer); + serialize(row.beta, printer); + serialize(row.gamma, printer); + } + return 0; } diff --git a/tests/MockDb.h b/tests/MockDb.h index 8642208e..64cbb883 100644 --- a/tests/MockDb.h +++ b/tests/MockDb.h @@ -104,24 +104,32 @@ struct MockDbT: public sqlpp::connection template size_t insert(const Insert& x) { + _serializer_context_t context; + ::sqlpp::serialize(x, context); return 0; } template size_t update(const Update& x) { + _serializer_context_t context; + ::sqlpp::serialize(x, context); return 0; } template size_t remove(const Remove& x) { + _serializer_context_t context; + ::sqlpp::serialize(x, context); return 0; } template - result_t select(const Select& s) + result_t select(const Select& x) { + _serializer_context_t context; + ::sqlpp::serialize(x, context); return {}; } @@ -138,24 +146,32 @@ struct MockDbT: public sqlpp::connection template _prepared_statement_t prepare_insert(Insert& x) { + _serializer_context_t context; + ::sqlpp::serialize(x, context); return nullptr; } template size_t run_prepared_insert(const PreparedInsert& x) { + _serializer_context_t context; + ::sqlpp::serialize(x, context); return 0; } template _prepared_statement_t prepare_select(Select& x) { + _serializer_context_t context; + ::sqlpp::serialize(x, context); return nullptr; } template result_t run_prepared_select(PreparedSelect& x) { + _serializer_context_t context; + ::sqlpp::serialize(x, context); return {}; } diff --git a/tests/ResultTest.cpp b/tests/ResultTest.cpp index 1353c791..bc01bd1b 100644 --- a/tests/ResultTest.cpp +++ b/tests/ResultTest.cpp @@ -47,6 +47,20 @@ int main() { static_assert(sqlpp::can_be_null_t::value, "row.alpha can be null"); static_assert(sqlpp::null_is_trivial_value_t::value, "row.alpha interprets null_is_trivial"); + static_assert(std::is_member_function_pointer::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"); + + bool x = sqlpp::rhs_is_null(t.alpha == row.alpha); + bool y = sqlpp::rhs_is_trivial(t.alpha == row.alpha); + std::cerr << x << std::endl; + std::cerr << y << std::endl; + + for (const auto& sub : db(select(all_of(t)).from(t).where(t.alpha == row.alpha))) + { + std::cerr << sub.alpha << std::endl; + } } sqlpp::select((t.alpha + 1).as(t.alpha)).flags(sqlpp::all).from(t); diff --git a/tests/SelectTest.cpp b/tests/SelectTest.cpp index 7bdf43a5..1e97562c 100644 --- a/tests/SelectTest.cpp +++ b/tests/SelectTest.cpp @@ -64,6 +64,11 @@ int main() std::cout << a << ", " << b << ", " << g << std::endl; } + for (const auto& row : db(select(all_of(t), all_of(f)).from(t.join(f).on(t.alpha > f.omega and not t.gamma)).where(true))) + { + std::cout << row.alpha << std::endl; + } + auto stat = sqlpp::select().columns(all_of(t)).flags(sqlpp::all).from(t).extra_tables(f,t).where(t.alpha > 0).group_by(t.alpha).order_by(t.gamma.asc()).having(t.gamma).limit(7).offset(19); auto s = dynamic_select(db).dynamic_columns(all_of(t)).dynamic_flags().dynamic_from(t).extra_tables(f,t).dynamic_where().dynamic_group_by(t.alpha).dynamic_order_by().dynamic_having(t.gamma).dynamic_limit().dynamic_offset();