diff --git a/.travis.yml b/.travis.yml index 9ec66b3f..07242b86 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ os: - linux - osx -dist: trusty +dist: xenial sudo: required compiler: @@ -23,6 +23,7 @@ notifications: install: - g++ --version + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew upgrade cmake; fi - cmake --version - git clone https://github.com/HowardHinnant/date - cd date diff --git a/README.md b/README.md index 6f7b64d3..a2ae3ffd 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,24 @@ brew install marvin182/zapfhahn/sqlpp11 Some connectors can be installed with the formula. See `brew info marvin182/zapfhahn/sqlpp11` for available options. +__Build via vcpkg:__ + +You can download and install sqlpp11 using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: + +```bash +git clone https://github.com/Microsoft/vcpkg.git +cd vcpkg +./bootstrap-vcpkg.sh +./vcpkg integrate install +vcpkg install sqlpp11 +``` + +The sqlpp11 port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. + +The following connector libraries for sqlpp11 are maintained as a separate package in vcpkg: + + * [sqlpp11-connector-sqlite3](https://github.com/microsoft/vcpkg/tree/master/ports/sqlpp11-connector-sqlite3) + * [sqlpp11-connector-mysql](https://github.com/microsoft/vcpkg/tree/master/ports/sqlpp11-connector-mysql) Basic usage: ------------- diff --git a/cmake/Sqlpp11Config.cmake b/cmake/Sqlpp11Config.cmake index 720fef2d..18ea9b1b 100644 --- a/cmake/Sqlpp11Config.cmake +++ b/cmake/Sqlpp11Config.cmake @@ -31,15 +31,14 @@ find_dependency(HinnantDate REQUIRED) include("${CMAKE_CURRENT_LIST_DIR}/Sqlpp11Targets.cmake") # Import "ddl2cpp" script -if(TARGET sqlpp11::ddl2cpp) - message(FATAL_ERROR "Target sqlpp11::ddl2cpp already defined") +if(NOT TARGET sqlpp11::ddl2cpp) + get_filename_component(sqlpp11_ddl2cpp_location "${CMAKE_CURRENT_LIST_DIR}/../../../bin/sqlpp11-ddl2cpp" REALPATH) + if(NOT EXISTS "${sqlpp11_ddl2cpp_location}") + message(FATAL_ERROR "The imported target sqlpp11::ddl2cpp references the file '${sqlpp11_ddl2cpp_location}' but this file does not exists.") + endif() + add_executable(sqlpp11::ddl2cpp IMPORTED) + set_target_properties(sqlpp11::ddl2cpp PROPERTIES + IMPORTED_LOCATION "${sqlpp11_ddl2cpp_location}" + ) + unset(sqlpp11_ddl2cpp_location) endif() -get_filename_component(sqlpp11_ddl2cpp_location "${CMAKE_CURRENT_LIST_DIR}/../../../bin/sqlpp11-ddl2cpp" REALPATH) -if(NOT EXISTS "${sqlpp11_ddl2cpp_location}") - message(FATAL_ERROR "The imported target sqlpp11::ddl2cpp references the file '${sqlpp11_ddl2cpp_location}' but this file does not exists.") -endif() -add_executable(sqlpp11::ddl2cpp IMPORTED) -set_target_properties(sqlpp11::ddl2cpp PROPERTIES - IMPORTED_LOCATION "${sqlpp11_ddl2cpp_location}" -) -unset(sqlpp11_ddl2cpp_location) diff --git a/include/sqlpp11/aggregate_function_operators.h b/include/sqlpp11/aggregate_function_operators.h new file mode 100644 index 00000000..6e92ad4c --- /dev/null +++ b/include/sqlpp11/aggregate_function_operators.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013-2020, Roland Bock, MacDue + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef SQLPP11_AGGREGATE_FUNCTION_OPERATORS_H +#define SQLPP11_AGGREGATE_FUNCTION_OPERATORS_H + +namespace sqlpp +{ + template + struct aggregate_function_operators + { + over_t over() const + { + return {*static_cast(this)}; + } + }; +} // namespace sqlpp + +#endif + diff --git a/include/sqlpp11/aggregate_functions/avg.h b/include/sqlpp11/aggregate_functions/avg.h index a9df9e62..f34ac297 100644 --- a/include/sqlpp11/aggregate_functions/avg.h +++ b/include/sqlpp11/aggregate_functions/avg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, Roland Bock + * Copyright (c) 2013-2020, Roland Bock, MacDue * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -56,6 +56,7 @@ namespace sqlpp template struct avg_t : public expression_operators, floating_point>, + public aggregate_function_operators>, public alias_operators> { using _traits = make_traits; diff --git a/include/sqlpp11/aggregate_functions/count.h b/include/sqlpp11/aggregate_functions/count.h index 79d96e52..91f7a830 100644 --- a/include/sqlpp11/aggregate_functions/count.h +++ b/include/sqlpp11/aggregate_functions/count.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, Roland Bock + * Copyright (c) 2013-2020, Roland Bock, MacDue * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -27,8 +27,10 @@ #ifndef SQLPP11_AGGREGATE_FUNCTIONS_COUNT_H #define SQLPP11_AGGREGATE_FUNCTIONS_COUNT_H +#include #include #include +#include #include namespace sqlpp @@ -57,6 +59,7 @@ namespace sqlpp template struct count_t : public expression_operators, integral>, + public aggregate_function_operators>, public alias_operators> { using _traits = make_traits; diff --git a/include/sqlpp11/aggregate_functions/max.h b/include/sqlpp11/aggregate_functions/max.h index 16faaa47..1c601809 100644 --- a/include/sqlpp11/aggregate_functions/max.h +++ b/include/sqlpp11/aggregate_functions/max.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, Roland Bock + * Copyright (c) 2013-2020, Roland Bock, MacDue * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -55,7 +55,9 @@ namespace sqlpp }; template - struct max_t : public expression_operators, value_type_of>, public alias_operators> + struct max_t : public expression_operators, value_type_of>, + public aggregate_function_operators>, + public alias_operators> { using _traits = make_traits, tag::is_expression, tag::is_selectable>; using _nodes = detail::type_vector; diff --git a/include/sqlpp11/aggregate_functions/min.h b/include/sqlpp11/aggregate_functions/min.h index 37604e03..54b663c8 100644 --- a/include/sqlpp11/aggregate_functions/min.h +++ b/include/sqlpp11/aggregate_functions/min.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, Roland Bock + * Copyright (c) 2013-2020, Roland Bock, MacDue * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -55,7 +55,9 @@ namespace sqlpp }; template - struct min_t : public expression_operators, value_type_of>, public alias_operators> + struct min_t : public expression_operators, value_type_of>, + public aggregate_function_operators>, + public alias_operators> { using _traits = make_traits, tag::is_expression, tag::is_selectable>; using _nodes = detail::type_vector; diff --git a/include/sqlpp11/aggregate_functions/sum.h b/include/sqlpp11/aggregate_functions/sum.h index 67428472..33396c28 100644 --- a/include/sqlpp11/aggregate_functions/sum.h +++ b/include/sqlpp11/aggregate_functions/sum.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, Roland Bock + * Copyright (c) 2013-2020, Roland Bock, MacDue * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -56,6 +56,7 @@ namespace sqlpp template struct sum_t : public expression_operators, value_type_of>, + public aggregate_function_operators>, public alias_operators> { using _traits = make_traits, tag::is_expression, tag::is_selectable>; diff --git a/include/sqlpp11/connection_pool.h b/include/sqlpp11/connection_pool.h index c63141ba..14e4e81b 100644 --- a/include/sqlpp11/connection_pool.h +++ b/include/sqlpp11/connection_pool.h @@ -183,7 +183,7 @@ namespace sqlpp try { - auto c = std::unique_ptr(new Connection(*(config.get()))); + auto c = std::unique_ptr(new Connection(config)); return pool_connection(c, this); } catch (const sqlpp::exception& e) diff --git a/include/sqlpp11/cte.h b/include/sqlpp11/cte.h index 9b3d40d4..eaa1129e 100644 --- a/include/sqlpp11/cte.h +++ b/include/sqlpp11/cte.h @@ -163,6 +163,7 @@ namespace sqlpp { using _traits = make_traits; // FIXME: is table? really? using _nodes = detail::type_vector<>; + using _provided_tables = detail::type_set; using _required_ctes = detail::make_joined_set_t, detail::type_set>; using _parameters = parameters_of; diff --git a/include/sqlpp11/data_types/blob/data_type.h b/include/sqlpp11/data_types/blob/data_type.h index c0137507..2758a761 100644 --- a/include/sqlpp11/data_types/blob/data_type.h +++ b/include/sqlpp11/data_types/blob/data_type.h @@ -28,6 +28,7 @@ #define SQLPP_BLOB_DATA_TYPE_H #include +#include #include #include diff --git a/include/sqlpp11/data_types/unsigned_integral/expression_operators.h b/include/sqlpp11/data_types/unsigned_integral/expression_operators.h index b72ce3cb..49f2a2f4 100644 --- a/include/sqlpp11/data_types/unsigned_integral/expression_operators.h +++ b/include/sqlpp11/data_types/unsigned_integral/expression_operators.h @@ -48,14 +48,14 @@ namespace sqlpp struct return_type_plus> { using check = consistent_t; - using type = value_type_of>; + using type = plus_t, value_type_of>, wrap_operand_t>; }; template struct return_type_minus> { using check = consistent_t; - using type = value_type_of>; + using type = minus_t, value_type_of>, wrap_operand_t>; }; template @@ -69,14 +69,14 @@ namespace sqlpp struct return_type_multiplies> { using check = consistent_t; - using type = value_type_of>; + using type = multiplies_t, value_type_of>, wrap_operand_t>; }; template struct return_type_divides> { using check = consistent_t; - using type = value_type_of>; + using type = divides_t, wrap_operand_t>; }; template diff --git a/include/sqlpp11/detail/type_set.h b/include/sqlpp11/detail/type_set.h index f5788354..343f3bcd 100644 --- a/include/sqlpp11/detail/type_set.h +++ b/include/sqlpp11/detail/type_set.h @@ -28,6 +28,7 @@ #define SQLPP11_DETAIL_TYPE_SET_H #include +#include #include #include diff --git a/include/sqlpp11/expression_fwd.h b/include/sqlpp11/expression_fwd.h index 220ab439..a4b55700 100644 --- a/include/sqlpp11/expression_fwd.h +++ b/include/sqlpp11/expression_fwd.h @@ -98,7 +98,17 @@ namespace sqlpp struct minus { using _traits = make_traits; - static constexpr const char* _name = "-"; + // The trailing space is necessary to prevent + // ``` + // t.id - -1 + // ``` + // from turning into + // ``` + // tab_sample.id--1 + // ``` + // (-- starts a comment in SQL) + // See https://github.com/rbock/sqlpp11/issues/294 + static constexpr const char* _name = " - "; }; template diff --git a/include/sqlpp11/over.h b/include/sqlpp11/over.h new file mode 100644 index 00000000..6a598e1e --- /dev/null +++ b/include/sqlpp11/over.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2013-2020, Roland Bock, MacDue + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLPP11_OVER_H +#define SQLPP11_OVER_H + +#include +#include + +namespace sqlpp +{ + template + struct over_t : public expression_operators, integral>, + public alias_operators> + { + using _traits = make_traits; + using _nodes = detail::type_vector; + + using _auto_alias_t = typename AggregateExpr::_auto_alias_t; + + over_t(AggregateExpr aggregate_expression) + : _aggregate_expression(aggregate_expression) + { + } + + over_t(const over_t&) = default; + over_t(over_t&&) = default; + over_t& operator=(const over_t&) = default; + over_t& operator=(over_t&&) = default; + ~over_t() = default; + + AggregateExpr _aggregate_expression; + }; + + template + struct serializer_t> + { + using _serialize_check = serialize_check_of; + using T = over_t; + + static Context& _(const T& t, Context& context) + { + serialize_operand(t._aggregate_expression, context); + context << " OVER()"; + return context; + } + }; +} // namespace sqlpp + +#endif diff --git a/include/sqlpp11/pre_join.h b/include/sqlpp11/pre_join.h index 15270b09..975b5042 100644 --- a/include/sqlpp11/pre_join.h +++ b/include/sqlpp11/pre_join.h @@ -30,6 +30,7 @@ #include #include #include +#include namespace sqlpp { @@ -154,7 +155,7 @@ namespace sqlpp auto join_impl(Check, Lhs lhs, Rhs rhs) -> inconsistent; template - auto join_impl(consistent_t, Lhs lhs, Rhs rhs) -> pre_join_t; + auto join_impl(consistent_t, Lhs lhs, Rhs rhs) -> pre_join_t, from_table_t>; template auto join_impl(Lhs lhs, Rhs rhs) -> decltype(join_impl(check_pre_join_t{}, lhs, rhs)); @@ -163,19 +164,19 @@ namespace sqlpp template auto join(Lhs lhs, Rhs rhs) -> decltype(detail::join_impl(lhs, rhs)) { - return {lhs, rhs}; + return {from_table(lhs), from_table(rhs)}; } template auto inner_join(Lhs lhs, Rhs rhs) -> decltype(detail::join_impl(lhs, rhs)) { - return {lhs, rhs}; + return {from_table(lhs), from_table(rhs)}; } template auto left_outer_join(Lhs lhs, Rhs rhs) -> decltype(detail::join_impl(lhs, rhs)) { - return {lhs, rhs}; + return {from_table(lhs), from_table(rhs)}; } template @@ -183,13 +184,13 @@ namespace sqlpp { check_pre_join_t{}; - return {lhs, rhs}; + return {from_table(lhs), from_table(rhs)}; } template auto outer_join(Lhs lhs, Rhs rhs) -> decltype(detail::join_impl(lhs, rhs)) { - return {lhs, rhs}; + return {from_table(lhs), from_table(rhs)}; } namespace detail @@ -202,13 +203,14 @@ namespace sqlpp -> join_t, on_t>; template - auto cross_join_impl(Lhs lhs, Rhs rhs) -> decltype(cross_join_impl(check_pre_join_t{}, lhs, rhs)); + auto cross_join_impl(Lhs lhs, Rhs rhs) + -> decltype(cross_join_impl(check_pre_join_t, from_table_t>{}, lhs, rhs)); } // namespace detail template auto cross_join(Lhs lhs, Rhs rhs) -> decltype(detail::cross_join_impl(lhs, rhs)) { - return {pre_join_t{lhs, rhs}, {}}; + return {pre_join_t, from_table_t>{from_table(lhs), from_table(rhs)}, {}}; } } // namespace sqlpp diff --git a/include/sqlpp11/trim.h b/include/sqlpp11/trim.h index 32c7a1e5..43b009a7 100644 --- a/include/sqlpp11/trim.h +++ b/include/sqlpp11/trim.h @@ -57,12 +57,11 @@ namespace sqlpp }; template - struct trim_t : public expression_operators, text>, - public alias_operators> + struct trim_t : public expression_operators, text>, public alias_operators> { using _traits = make_traits; - using _nodes = detail::type_vector; + using _nodes = detail::type_vector; using _can_be_null = can_be_null_t; using _is_aggregate_expression = std::false_type; @@ -94,7 +93,6 @@ namespace sqlpp context << ")"; return context; } - }; template diff --git a/include/sqlpp11/where.h b/include/sqlpp11/where.h index d79d390e..2d7298d0 100644 --- a/include/sqlpp11/where.h +++ b/include/sqlpp11/where.h @@ -256,6 +256,10 @@ namespace sqlpp static_check_t::value, assert_where_dynamic_used_with_dynamic_statement_t>, check_where_t>; + template + using check_where_empty_dynamic_t = static_combined_check_t< + static_check_t::value, assert_where_dynamic_used_with_dynamic_statement_t>>; + // NO WHERE YET template struct no_where_t @@ -338,10 +342,11 @@ namespace sqlpp return _where_impl<_database_t>(Check{}, expression); } - auto dynamic_where() const -> _new_statement_t, - where_t<_database_t, boolean_operand>> + auto dynamic_where() const + -> _new_statement_t, where_t<_database_t, unconditional_t>> { - return dynamic_where(::sqlpp::value(true)); + return {static_cast&>(*this), + where_data_t<_database_t, unconditional_t>{unconditional_t{}}}; } private: @@ -378,6 +383,24 @@ namespace sqlpp } }; + template + struct serializer_t> + { + using _serialize_check = consistent_t; + using T = where_data_t; + + static Context& _(const T& t, Context& context) + { + if (t._dynamic_expressions.empty()) + { + return context; + } + context << " WHERE "; + interpret_list(t._dynamic_expressions, " AND ", context); + return context; + } + }; + template struct serializer_t> { @@ -403,6 +426,13 @@ namespace sqlpp return statement_t>().dynamic_where(std::forward(t)); } + template + auto dynamic_where(const Database & /*unused*/) + -> decltype(statement_t>().dynamic_where()) + { + return statement_t>().dynamic_where(); + } + inline auto unconditionally() -> decltype(statement_t>().unconditionally()) { return statement_t>().unconditionally(); diff --git a/scripts/ddl2cpp b/scripts/ddl2cpp index f4762a83..113a80a7 100755 --- a/scripts/ddl2cpp +++ b/scripts/ddl2cpp @@ -273,6 +273,7 @@ types = { 'numeric': 'floating_point', # PostgreSQL 'date': 'day_point', 'datetime': 'time_point', + 'time': 'time_of_day', 'time without time zone': 'time_point', # PostgreSQL 'time with time zone': 'time_point', # PostgreSQL 'timestamp': 'time_point', @@ -311,6 +312,8 @@ nsList = namespace.split('::') def escape_if_reserved(name): reserved_names = [ + 'BEGIN', + 'END', 'GROUP', 'ORDER', ] diff --git a/scripts/sqlite2cpp.py b/scripts/sqlite2cpp.py index 71c5c5a8..c6f243c2 100644 --- a/scripts/sqlite2cpp.py +++ b/scripts/sqlite2cpp.py @@ -48,6 +48,11 @@ def main(): parser.add_argument('ddl', help='path to ddl') parser.add_argument('target', help='path to target') parser.add_argument('namespace', help='namespace') + parser.add_argument('-identity-naming', + help='Use table and column names from the ddl ' + '(defaults to UpperCamelCase for tables and ' + 'lowerCamelCase for columns)', + action='store_true') args = parser.parse_args() pathToHeader = args.target + '.h' @@ -57,8 +62,8 @@ def main(): conn.executescript(open(args.ddl).read()) # set vars - toClassName = class_name_naming_func - toMemberName = member_name_naming_func + toClassName = identity_naming_func if args.identity_naming else class_name_naming_func + toMemberName = identity_naming_func if args.identity_naming else member_name_naming_func DataTypeError = False header = open(pathToHeader, 'w') @@ -173,6 +178,9 @@ def get_include_guard_name(namespace, inputfile): val = re.sub("[^A-Za-z0-9]+", "_", namespace + '_' + os.path.basename(inputfile)) return val.upper() +def identity_naming_func(s): + return s + def repl_camel_case_func(m): if m.group(1) == '_': return m.group(2).upper() diff --git a/test_scripts/CMakeLists.txt b/test_scripts/CMakeLists.txt index f2f4c34f..0a8c2b46 100644 --- a/test_scripts/CMakeLists.txt +++ b/test_scripts/CMakeLists.txt @@ -22,11 +22,11 @@ # (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(FindPythonInterp) +include(FindPython3) -if (${PYTHONINTERP_FOUND}) +if (${Python3_Interpreter_FOUND}) execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "import pyparsing" + COMMAND ${Python3_EXECUTABLE} -c "import pyparsing" RESULT_VARIABLE PythonRESULT OUTPUT_VARIABLE PythonOUTPUT ERROR_VARIABLE PythonERROR @@ -39,21 +39,21 @@ if (${PYTHONINTERP_FOUND}) message(STATUS "Pyparsing is installed: Enabling ddl2cpp tests.") add_test(NAME sqlpp11.test.ddl2cpp.bad_will_fail - COMMAND "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_LIST_DIR}/../scripts/ddl2cpp" -fail-on-parse + COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_LIST_DIR}/../scripts/ddl2cpp" -fail-on-parse "${CMAKE_CURRENT_LIST_DIR}/ddl2cpp_sample_bad.sql" "${CMAKE_CURRENT_BINARY_DIR}/fail" test) set_tests_properties(sqlpp11.test.ddl2cpp.bad_will_fail PROPERTIES WILL_FAIL 1) add_test(NAME sqlpp11.test.ddl2cpp.bad_has_parse_error - COMMAND "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_LIST_DIR}/../scripts/ddl2cpp" -fail-on-parse + COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_LIST_DIR}/../scripts/ddl2cpp" -fail-on-parse "${CMAKE_CURRENT_LIST_DIR}/ddl2cpp_sample_bad.sql" "${CMAKE_CURRENT_BINARY_DIR}/fail" test) set_tests_properties(sqlpp11.test.ddl2cpp.bad_has_parse_error PROPERTIES PASS_REGULAR_EXPRESSION "Parsing error,.*") add_test(NAME sqlpp11.test.ddl2cpp.good_succeeds - COMMAND "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_LIST_DIR}/../scripts/ddl2cpp" -fail-on-parse + COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_LIST_DIR}/../scripts/ddl2cpp" -fail-on-parse "${CMAKE_CURRENT_LIST_DIR}/ddl2cpp_sample_good.sql" "${CMAKE_CURRENT_BINARY_DIR}/fail" test) @@ -67,7 +67,7 @@ if (${PYTHONINTERP_FOUND}) endif() add_custom_command( OUTPUT "${sqlpp.test.generated.sample.include}.h" - COMMAND "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_LIST_DIR}/../scripts/ddl2cpp" + COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_LIST_DIR}/../scripts/ddl2cpp" ${use_identity_naming} "${CMAKE_CURRENT_LIST_DIR}/ddl2cpp_sample_good.sql" "${sqlpp.test.generated.sample.include}" diff --git a/test_serializer/As.cpp b/test_serializer/As.cpp index 86352257..25f6783f 100644 --- a/test_serializer/As.cpp +++ b/test_serializer/As.cpp @@ -31,18 +31,18 @@ SQLPP_ALIAS_PROVIDER(cheese) -int As(int, char* []) +int As(int, char*[]) { const auto foo = test::TabFoo{}; const auto bar = test::TabBar{}; compare(__LINE__, foo.omega.as(cheese), "tab_foo.omega AS cheese"); compare(__LINE__, (foo.omega + 17).as(cheese), "(tab_foo.omega+17) AS cheese"); - compare(__LINE__, (foo.omega - 17).as(cheese), "(tab_foo.omega-17) AS cheese"); - compare(__LINE__, (foo.omega - uint32_t(17)).as(cheese), "(tab_foo.omega-17) AS cheese"); - compare(__LINE__, (foo.omega - bar.alpha).as(cheese), "(tab_foo.omega-tab_bar.alpha) AS cheese"); - compare(__LINE__, (count(foo.omega) - bar.alpha).as(cheese), "(COUNT(tab_foo.omega)-tab_bar.alpha) AS cheese"); - compare(__LINE__, (count(foo.omega) - uint32_t(17)).as(cheese), "(COUNT(tab_foo.omega)-17) AS cheese"); + compare(__LINE__, (foo.omega - 17).as(cheese), "(tab_foo.omega - 17) AS cheese"); + compare(__LINE__, (foo.omega - uint32_t(17)).as(cheese), "(tab_foo.omega - 17) AS cheese"); + compare(__LINE__, (foo.omega - bar.alpha).as(cheese), "(tab_foo.omega - tab_bar.alpha) AS cheese"); + compare(__LINE__, (count(foo.omega) - bar.alpha).as(cheese), "(COUNT(tab_foo.omega) - tab_bar.alpha) AS cheese"); + compare(__LINE__, (count(foo.omega) - uint32_t(17)).as(cheese), "(COUNT(tab_foo.omega) - 17) AS cheese"); // Auto alias compare(__LINE__, select(max(bar.alpha)), "SELECT MAX(tab_bar.alpha) AS max_"); diff --git a/test_serializer/CMakeLists.txt b/test_serializer/CMakeLists.txt index 2e20950c..b17c83ef 100644 --- a/test_serializer/CMakeLists.txt +++ b/test_serializer/CMakeLists.txt @@ -26,11 +26,13 @@ set(test_serializer_names As Blob CustomQuery + DynamicWhere ForUpdate From In Insert - TableAlias + Over + TableAlias Where ) @@ -44,7 +46,7 @@ if (SQLPP11_TESTS_CXX_STD) set_property(TARGET sqlpp11_test_serializer PROPERTY CXX_STANDARD_REQUIRED yes) set_property(TARGET sqlpp11_test_serializer PROPERTY CXX_EXTENSIONS no) endif() - + foreach(test_serializer IN LISTS test_serializer_names) add_test(NAME sqlpp11.test_serializer.${test_serializer} COMMAND sqlpp11_test_serializer ${test_serializer} diff --git a/test_serializer/DynamicWhere.cpp b/test_serializer/DynamicWhere.cpp new file mode 100644 index 00000000..3fa9b0dc --- /dev/null +++ b/test_serializer/DynamicWhere.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016-2019, 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 "compare.h" +#include "Sample.h" +#include + +#include + +int DynamicWhere(int, char*[]) +{ + const auto bar = test::TabBar{}; + auto db = MockDb{}; + + compare(__LINE__, dynamic_where(db), ""); + compare(__LINE__, dynamic_where(db, bar.gamma), " WHERE tab_bar.gamma"); + + { + auto statement = sqlpp::dynamic_where(db); + statement.where.add(without_table_check(bar.gamma)); + compare(__LINE__, statement, " WHERE tab_bar.gamma"); + } + + { + auto statement = dynamic_where(db, bar.gamma); + statement.where.add(without_table_check(bar.gamma)); + compare(__LINE__, statement, " WHERE tab_bar.gamma AND tab_bar.gamma"); + } + + return 0; +} diff --git a/test_serializer/Over.cpp b/test_serializer/Over.cpp new file mode 100644 index 00000000..c1afb69e --- /dev/null +++ b/test_serializer/Over.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016-2020, Roland Bock, MacDue + * 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 "compare.h" +#include + +SQLPP_ALIAS_PROVIDER(dueutil) + +int Over(int, char* []) { + auto const foo = test::TabFoo{}; + + // no/auto alias (wrapped in select so alias is applied) + compare(__LINE__, select(avg(foo.omega).over()), "SELECT AVG(tab_foo.omega) OVER() AS avg_"); + compare(__LINE__, select(count(foo.omega).over()), "SELECT COUNT(tab_foo.omega) OVER() AS count_"); + compare(__LINE__, select(max(foo.omega).over()), "SELECT MAX(tab_foo.omega) OVER() AS max_"); + compare(__LINE__, select(min(foo.omega).over()), "SELECT MIN(tab_foo.omega) OVER() AS min_"); + compare(__LINE__, select(sum(foo.omega).over()), "SELECT SUM(tab_foo.omega) OVER() AS sum_"); + + // alias + compare(__LINE__, avg(foo.omega).over().as(dueutil), "AVG(tab_foo.omega) OVER() AS dueutil"); + compare(__LINE__, count(foo.omega).over().as(dueutil), "COUNT(tab_foo.omega) OVER() AS dueutil"); + compare(__LINE__, max(foo.omega).over().as(dueutil), "MAX(tab_foo.omega) OVER() AS dueutil"); + compare(__LINE__, min(foo.omega).over().as(dueutil), "MIN(tab_foo.omega) OVER() AS dueutil"); + compare(__LINE__, sum(foo.omega).over().as(dueutil), "SUM(tab_foo.omega) OVER() AS dueutil"); + + return 0; +} diff --git a/test_serializer/Where.cpp b/test_serializer/Where.cpp index ff369a99..6e568196 100644 --- a/test_serializer/Where.cpp +++ b/test_serializer/Where.cpp @@ -42,9 +42,9 @@ namespace MockDb::_serializer_context_t printer = {}; return serialize(sqlpp::value(false), printer).str(); } -} +} // namespace -int Where(int, char* []) +int Where(int, char*[]) { const auto foo = test::TabFoo{}; const auto bar = test::TabBar{}; @@ -53,6 +53,8 @@ int Where(int, char* []) compare(__LINE__, select(foo.omega).from(foo).unconditionally(), "SELECT tab_foo.omega FROM tab_foo"); compare(__LINE__, remove_from(foo).unconditionally(), "DELETE FROM tab_foo"); compare(__LINE__, update(foo).set(foo.omega = 42).unconditionally(), "UPDATE tab_foo SET omega=42"); + compare(__LINE__, update(foo).set(foo.omega = foo.omega - -1).unconditionally(), + "UPDATE tab_foo SET omega=(tab_foo.omega - -1)"); compare(__LINE__, where(sqlpp::value(true)), " WHERE " + getTrue()); // Never diff --git a/tests/Sample.h b/tests/Sample.h index cbb738fa..d0119149 100644 --- a/tests/Sample.h +++ b/tests/Sample.h @@ -256,7 +256,6 @@ namespace test }; }; }; - namespace TabDateTime_ { struct ColDayPoint @@ -303,7 +302,6 @@ namespace test }; using _traits = sqlpp::make_traits; }; - struct ColTimeOfDay { struct _alias_t @@ -333,7 +331,7 @@ namespace test { struct _alias_t { - static constexpr const char _literal[] = "tab_time_point"; + static constexpr const char _literal[] = "tab_date_time"; using _name_t = sqlpp::make_char_sequence; template struct _member_t diff --git a/tests/Select.cpp b/tests/Select.cpp index 90f25f70..0860479e 100644 --- a/tests/Select.cpp +++ b/tests/Select.cpp @@ -61,7 +61,7 @@ void print_row(Row const& row) std::cout << a << ", " << b << std::endl; } -int Select(int, char* []) +int Select(int, char*[]) { MockDb db = {}; MockDb::_serializer_context_t printer = {}; @@ -107,11 +107,12 @@ int Select(int, char* []) std::cout << a << ", " << b << ", " << g << std::endl; } - for (const auto& row : db(select(all_of(t).as(t), t.gamma).from(t).where(t.alpha > 7).for_update())) + 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.tabBar.alpha; - const std::string b = row.tabBar.beta; - const bool g = row.gamma; + int64_t a = row.alpha; + const std::string b = row.beta; + const bool g = row.tabBar; std::cout << a << ", " << b << ", " << g << std::endl; } @@ -208,20 +209,19 @@ int Select(int, char* []) } { - auto transaction = start_transaction(db, sqlpp::isolation_level::read_committed); - if (db._mock_data._last_isolation_level != sqlpp::isolation_level::read_committed) - { - std::cout << "Error: transaction isolation level does not match expected level" << std::endl; - } - + auto transaction = start_transaction(db, sqlpp::isolation_level::read_committed); + if (db._mock_data._last_isolation_level != sqlpp::isolation_level::read_committed) + { + std::cout << "Error: transaction isolation level does not match expected level" << std::endl; + } } db.set_default_isolation_level(sqlpp::isolation_level::read_uncommitted); { - auto transaction = start_transaction(db); - if (db._mock_data._last_isolation_level != sqlpp::isolation_level::read_uncommitted) - { - std::cout << "Error: transaction isolation level does not match default level" << std::endl; - } + auto transaction = start_transaction(db); + if (db._mock_data._last_isolation_level != sqlpp::isolation_level::read_uncommitted) + { + std::cout << "Error: transaction isolation level does not match default level" << std::endl; + } } return 0; diff --git a/tests/SelectType.cpp b/tests/SelectType.cpp index f783ce15..e98febbb 100644 --- a/tests/SelectType.cpp +++ b/tests/SelectType.cpp @@ -39,9 +39,9 @@ namespace alias SQLPP_ALIAS_PROVIDER(b) SQLPP_ALIAS_PROVIDER(left) SQLPP_ALIAS_PROVIDER(right) -} +} // namespace alias -int SelectType(int, char* []) +int SelectType(int, char*[]) { MockDb db = {}; MockDb::_serializer_context_t printer = {}; @@ -163,7 +163,8 @@ int SelectType(int, char* []) static_assert(sqlpp::is_integral_t>::value, "type requirement"); static_assert(sqlpp::is_integral_t>::value, "type requirement"); static_assert(sqlpp::is_integral_t>::value, "type requirement"); - static_assert(sqlpp::is_integral_t>::value, "type requirement"); + static_assert(sqlpp::is_floating_point_t>::value, + "type requirement"); static_assert(sqlpp::is_integral_t>::value, "type requirement"); static_assert(sqlpp::is_integral_t>::value, "type requirement"); static_assert(sqlpp::is_integral_t>::value, "type requirement"); diff --git a/tests/Update.cpp b/tests/Update.cpp index 5f9a6345..fb4c522e 100644 --- a/tests/Update.cpp +++ b/tests/Update.cpp @@ -66,6 +66,9 @@ int Update(int, char*[]) auto values = [&t]() { return std::make_tuple(t.delta += t.alpha, t.beta = "no cake this time"); }; db(update(t).set(t.delta = sqlpp::verbatim("17+4")).unconditionally()); + db(update(t) + .set(t.delta = sqlpp::verbatim("17+4")) + .where(sqlpp::verbatim("'hansi'") == "hansi")); db(update(t).set(t.delta = sqlpp::null).unconditionally()); db(update(t).set(t.delta = sqlpp::default_value).unconditionally()); diff --git a/tests/With.cpp b/tests/With.cpp index dae27867..fbd2995f 100644 --- a/tests/With.cpp +++ b/tests/With.cpp @@ -55,5 +55,24 @@ int With(int, char*[]) sqlpp::cte(b).as(select(t.alpha.as(a)).from(t).unconditionally().union_all(select(sqlpp::value(123).as(a)))); db(with(c)(select(all_of(c)).from(c).unconditionally())); + // recursive CTE with join + { + const auto selectBase = select(t.alpha, t.delta).from(t).where(t.alpha > 17); + const auto initialCte = ::sqlpp::cte(sqlpp::alias::a).as(selectBase); + const auto recursiveCte = initialCte.union_all( + select(t.alpha, t.delta).from(t.join(initialCte).on(t.alpha == initialCte.delta)).unconditionally()); + const auto query = with(recursiveCte)(select(recursiveCte.alpha).from(recursiveCte).unconditionally()); + + ::MockDb::_serializer_context_t printer = {}; + const auto serializedQuery = serialize(query, printer).str(); + std::cout << serializedQuery << '\n'; + + auto db = MockDb{}; + for (const auto& row : db(query)) + { + std::cout << row.alpha; + } + } + return 0; } diff --git a/tests/sample.sql b/tests/sample.sql index f46f7dd7..5cdacab2 100644 --- a/tests/sample.sql +++ b/tests/sample.sql @@ -26,10 +26,11 @@ CREATE TABLE IF NOT EXISTS tab_foo ( - delta varchar(255), + delta varchar(255) NOT NULL DEFAULT "", epsilon bigint, omega double, - psi bigint UNSIGNED + psi bigint UNSIGNED, + book BLOB ); CREATE TABLE tab_bar @@ -40,3 +41,10 @@ CREATE TABLE tab_bar delta int ); +CREATE TABLE tab_date_time +( + col_day_point date, + col_time_point datetime, + col_time_of_day time +); + diff --git a/wishlist.md b/wishlist.md new file mode 100644 index 00000000..3d13d474 --- /dev/null +++ b/wishlist.md @@ -0,0 +1,73 @@ +If you want to help with the development of the library, you might want to consider one of these items: + +# Better documentation +The documentation in the Wiki is incomplete and outdated. It is also not part of source control, which makes it a bit harder to contribute. +It might make sense to write a couple of markdown files here. + +Throw in chapters of how to add features and how to write connector libraries. + +## Better tests and examples +Many of the current tests were born when the library was in infant state. Often they test implementation details. And they often just print stuff, instead of checking said stuff. + +# Connectors +## Merge stable connectors into sqlpp11 +Having all connector libraries in place, makes maintenance considerably simpler, see #174. +It would also reduce the cognitive overhead for newcomers. + +## New connectors +There are a couple of connector libraries already. Here are some that have been requested in the past + +- Sybase +- Oracle +- SQL Server +- Google Spanner + +## More special functions and types for connectors +GROUP CONCAT or JSON support for mysql, for instance +INSERT OR UPDATE for postgresl and mysql +INSERT INTO ... SELECT ... for postgresql +CREATE TABLE for all of them, including PPGEN + +Exception types that carry more specific error information, e.g. the native error code, see #227 + +Find a way to handle column names that are keywords in the vendor's dialect, see #199 + +Handle specific data types, including JSON for the connectors. + +More test cases for DDL files. + +# EDSL features +Multi-line insert in prepared statements, see #68 + +## optional std::optional support +Instead of sqlpp::value_or_null, std::optional would be nice, see #238 + +## Converter to and from SQL to C++ structs, to allow for more ORM like code +Assuming that you have converter functions from struct to sqlpp11 and back, we could have something like this: +```C++ +struct Person; +insert_into(tab).set(Person{}); +update(tab).set(Person{}); +for (const auto& person : db(select(Person{}).from(tab).unconditionally())) +{ + // ... +} +``` + +# Runtime improvements +## Connection pools and caching interface +In order to support high load scenarios, + - connection pools (to avoid creating and destroying connections) + - caching (hash the query and answer it from the cache for some time), see #86 + +## Async support +Obtain results in an asynchronous fashion, see #35, for instance. + +# Compile time improvements +## Simplify code +See Seventeenification talk. Some of the simplifications can be ported back easily. + +## Suppress export of symbols +Some compilers tend to export all the generated symbols, which is a bit annoying in case of template-heavy libraries like sqlpp11 (leads to larger files and longer compile/link/startup times, I believe). +There are ways to suppress this in most compilers, afaik. +