diff --git a/.travis.yml b/.travis.yml index cf0c1753..ce830dcb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,9 @@ install: - CMAKE_VERSION_MM=3.2 - CMAKE_VERSION_FULL=$CMAKE_VERSION_MM.2 - git clone https://github.com/HowardHinnant/date + - cd date + - git checkout tags/v1.0.0 + - cd .. - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test && sudo add-apt-repository -y ppa:apokluda/boost1.53 diff --git a/CMakeLists.txt b/CMakeLists.txt index f606df00..58ab3755 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2015, Roland Bock +# Copyright (c) 2013-2016, Roland Bock # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, @@ -32,12 +32,19 @@ add_library(sqlpp11 INTERFACE) set(DATE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../date" CACHE FILEPATH "Path to Howard Hinnant's date library") if(NOT EXISTS ${DATE_INCLUDE_DIR}/date.h) - message(SEND_ERROR "Can't find date.h in ${DATE_INCLUDE_DIR}") + message(SEND_ERROR "Can't find file date.h and cannot compile date_test/date_test.cpp") + message("Can't find date.h in ${DATE_INCLUDE_DIR} ") + message("Please either") + message(" - git clone https://github.com/howardhinnant/date ${DATE_INCLUDE_DIR}") + message(" - download and unzip a current version from https://github.com/howardhinnant/date to ${DATE_INCLUDE_DIR}") + message(" - set DATE_INCLUDE_DIR to point to the dir containing date.h from the date library") + message("") endif() target_include_directories(sqlpp11 INTERFACE $ $ + $ ) if (NOT MSVC) @@ -64,10 +71,51 @@ target_compile_features(sqlpp11 INTERFACE ) endif () +install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/sqlpp11" + DESTINATION include +) + +install(TARGETS sqlpp11 + EXPORT Sqlpp11Targets +) + +include(CMakePackageConfigHelpers) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/cmake/Sqlpp11ConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion +) + +export(EXPORT Sqlpp11Targets + FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/Sqlpp11Targets.cmake" +) + +configure_file(cmake/Sqlpp11Config.cmake + "${CMAKE_CURRENT_BINARY_DIR}/cmake/Sqlpp11Config.cmake" + COPYONLY +) + +set(ConfigPackageLocation lib/cmake/Sqlpp11) +install(EXPORT Sqlpp11Targets + FILE + Sqlpp11Targets.cmake + DESTINATION + ${ConfigPackageLocation} +) + +install( + FILES + cmake/Sqlpp11Config.cmake + "${CMAKE_CURRENT_BINARY_DIR}/cmake/Sqlpp11ConfigVersion.cmake" + DESTINATION + ${ConfigPackageLocation} +) + + add_subdirectory(tests) +add_subdirectory(test_types) +add_subdirectory(test_serializer) add_subdirectory(test_static_asserts) add_subdirectory(test_constraints) add_subdirectory(examples) - -install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/sqlpp11" DESTINATION include) - diff --git a/ChangeLog.md b/ChangeLog.md new file mode 100644 index 00000000..8aa06f0b --- /dev/null +++ b/ChangeLog.md @@ -0,0 +1,109 @@ +Important changes in sqlpp11 +============================ + +Breaking changes in 0.36: +------------------------- +__Abstract__: + +One of the main motivations of sqlpp11 is to prevent SQL programming mistakes at compile time. The following changes prevent reported mishaps. + * `from(a,b)` not allowed anymore, please use explicit joins + * `where(true)` not allowed anymore, please use `.unconditionally()` or sqlpp::value(true) + * `some_sql_expression and true` not allowed anymore, please use `tab.col == sqlpp::value(true)` if you really want to express this. + * `having(expression)` requires `expression` to be made of aggregates, e.g. columns named in `group_by()` or aggregate functions like `count()` or constant values. + * `where()` and `having` accept only one parameter + +__Explicit joins required__: + +Up until sqlpp11-0.35 you could write something like + +``` +auto result = db(select(all_of(a), all_of(b)) + .from(a,b) + .where(true)); +``` + +Using this syntax in `from()`, it was expected to have the join condition implicitly in the `where()` call. +But there is no reliable way to tell whether or not that condition is there. Or, if there definitely is none, whether +it was omitted on purpose or by accident. +In one case, an accidentally omitted join condition in the `where()` brought a production system to a screeching halt. + +In order to prevent this in the future, sqlpp11 now requires you to join table explicitly, including an explicit join condition, e.g. + +``` +auto result = db(select(all_of(a), all_of(b)) + .from(a.join(b).on(a.b == b.id)) + .where(true)); +``` + +Most joins, (`join`/`inner_join`, `left_outer_join`, `right_outer_join` and `outer_join`) require a join condition to given via `on()`. +The join condition has to be some sqlpp11 boolean expression. + +In those rare cases, when you really need a cross join, you can also use `cross_join()` which has no join condition, of course. + +``` +auto result = db(select(all_of(a), all_of(b)) + .from(a.cross_join(b)) + .where(true)); +``` + +__Use `.unconditionally()`__ + +If you want to select/update/remove all rows, earlier versions of sqlpp11 required the use of `where(true)`. Since version 0.36, use `unconditionally()`, for instance: +``` +auto result = db(select(all_of(t)).from(t).unconditionally()); +``` + +__Use `sqlpp::value()` to wrap bool values in boolean expressions__ + +Lets say you had + +``` +struct X +{ + int a; + int b; +}; +auto x = X{}; +``` + +Then earlier versions of sqlpp11 would compile the following expression: + +``` +select(all_of(t)).from(t).where(x.a == x.a or t.b == t.b); +``` + +What you probably meant was: + +``` +select(all_of(t)).from(t).where(t.a == x.a and t.b == x.b); +``` + +In order to prevent this kind of mistake, boolean operators in sql expressions require sqlpp boolean expressions as operators. +The library also requires the types of the left/right hand side operands of a comparison to be different, so that `t.a < t.a` does not compile any more. + +In the rare case you really have a bool value that you want to use a boolean sql expression, you have to wrap it in sqlpp::value(x), e.g. + +``` +select(all_of(t)).from(t).where(sqlpp::value(x.a == 17) and t.b == x.b); +``` + +__`having()` requires aggregate expressions__ + +In older versions, the following code was allowed: + +``` +select(all_of(t)).from(t).where(t.a > 7).having(t.b != ""); +``` + +As of sqlpp11-0.36, the having argument must be made of aggregate columns or functions, e.g. + +``` +select(all_of(t)).from(t).unconditionally().group_by(t.b).having(t.b != "", avg(t.c) < 42); +``` + +__`where()` and `having` accept only one expression__ + +In older versions, `where()` and `having()` would accept more than one argument and combine those arguments with `and`. +I am not sure this was ever used. So it just made life harder for the compiler. + +As of version 0.36, `where()` and `having()` accept only one parameter. diff --git a/README.md b/README.md index 25c8bb2a..bf637473 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,22 @@ sqlpp11 ======= +A type safe embedded domain specific language for SQL queries and results in C++ -Branch / Compiler | clang-3.4, gcc-4.9, Xcode-7 | MSVC 2015 | Test Coverage +Documentation is found in the [wiki](https://github.com/rbock/sqlpp11/wiki) + + +Breaking changes in 0.36: +------------------------- +See [Changes](ChangeLog.md) + +Status: +------- + +Branch / Compiler | clang-3.4, gcc-4.9, Xcode-7 | MSVC 2015 | Test Coverage ------------------| -------------------------------|-------------|--------------- master | [![Build Status](https://travis-ci.org/rbock/sqlpp11.svg?branch=master)](https://travis-ci.org/rbock/sqlpp11?branch=master) | [![Build status](https://ci.appveyor.com/api/projects/status/eid7mwqgavo0h61h/branch/master?svg=true)](https://ci.appveyor.com/project/rbock/sqlpp11/branch/master) | [![Coverage Status](https://coveralls.io/repos/rbock/sqlpp11/badge.svg?branch=master)](https://coveralls.io/r/rbock/sqlpp11?branch=master) develop | [![Build Status](https://travis-ci.org/rbock/sqlpp11.svg?branch=develop)](https://travis-ci.org/rbock/sqlpp11?branch=develop) | [![Build status](https://ci.appveyor.com/api/projects/status/eid7mwqgavo0h61h/branch/develop?svg=true)](https://ci.appveyor.com/project/rbock/sqlpp11/branch/develop) | [![Coverage Status](https://coveralls.io/repos/rbock/sqlpp11/badge.svg?branch=develop)](https://coveralls.io/r/rbock/sqlpp11?branch=develop) -A type safe embedded domain specific language for SQL queries and results in C++ - -Documentation is found in the wiki, https://github.com/rbock/sqlpp11/wiki - -Past talks about sqlpp11 and some coding concepts used within the library: - - * [CppCast:](http://cppcast.com) - * 2015-05-07: http://cppcast.com/2015/05/roland-bock/ - * [CppCon:](http://cppcon.org) - * 2015-09-24: [Pruning Error Messages From Your C++ Template Code](https://www.youtube.com/watch?v=2ISqFW9fRws), with examples from sqlpp11 - * 2014-09-11: [sqlpp11, An SQL Library Worthy Of Modern C++](https://www.youtube.com/watch?v=cJPAjhBm-HQ) - * [Meeting C++:](http://meetingcpp.com) - * 2014-12-05: [sqlpp11, An EDSL For Type-Safe SQL In C++11](https://www.youtube.com/watch?v=9Hjfg9IfzhU) - * [MUC++:](http://www.meetup.com/MUCplusplus/) - * 2014-02-27: [Selected C++11 Template Toffees From sqlpp11, Part1](https://www.youtube.com/watch?v=hXnGFYNbmXg), [Part2](https://www.youtube.com/watch?v=WPCV6dvxZ_U), [Part 3](https://www.youtube.com/watch?v=eB7hd_KjTig), [Part 4](https://www.youtube.com/watch?v=NBfqzcN0_EQ) - -You can contact me - * by posting issues at https://github.com/rbock/sqlpp11/issues - * or via email at rbock at eudoxos dot de - Motivation: ----------- SQL and C++ are both strongly typed languages. Still, most C/C++ interfaces to SQL are based on constructing queries as strings and on interpreting arrays or maps of strings as results. @@ -46,6 +37,8 @@ The library supports both static and dynamic queries. The former offers greater sqlpp11 is vendor-neutral. Specific traits of databases (e.g. unsupported or non-standard features) are handled by connector libraries. Connector libraries can inform the developer of missing features at compile time. They also interpret expressions specifically where needed. For example, the connector could use the operator|| or the concat method for string concatenation without the developer being required to change the statement. +The library is already used in production but it is certainly not complete yet. Feature requests, bug reports, contributions to code or documentation are most welcome. + Examples: --------- For the examples, lets assume you have a table class representing something like @@ -102,9 +95,23 @@ db(update(foo).set(foo.hasFun = not foo.hasFun).where(foo.name != "nobody")); db(remove_from(foo).where(not foo.hasFun)); ``` -Your help is needed: --------------------- -The library is already used in production but it is certainly not complete yet. Feature requests, bug reports, contributions to code or documentation are most welcome. +Additional information available: +--------------------------------- +Past talks about sqlpp11 and some coding concepts used within the library: + + * [CppCast:](http://cppcast.com) + * 2015-05-07: http://cppcast.com/2015/05/roland-bock/ + * [CppCon:](http://cppcon.org) + * 2015-09-24: [Pruning Error Messages From Your C++ Template Code](https://www.youtube.com/watch?v=2ISqFW9fRws), with examples from sqlpp11 + * 2014-09-11: [sqlpp11, An SQL Library Worthy Of Modern C++](https://www.youtube.com/watch?v=cJPAjhBm-HQ) + * [Meeting C++:](http://meetingcpp.com) + * 2014-12-05: [sqlpp11, An EDSL For Type-Safe SQL In C++11](https://www.youtube.com/watch?v=9Hjfg9IfzhU) + * [MUC++:](http://www.meetup.com/MUCplusplus/) + * 2014-02-27: [Selected C++11 Template Toffees From sqlpp11, Part1](https://www.youtube.com/watch?v=hXnGFYNbmXg), [Part2](https://www.youtube.com/watch?v=WPCV6dvxZ_U), [Part 3](https://www.youtube.com/watch?v=eB7hd_KjTig), [Part 4](https://www.youtube.com/watch?v=NBfqzcN0_EQ) + +You can contact me + * by posting issues at https://github.com/rbock/sqlpp11/issues + * or via email at rbock at eudoxos dot de Requirements: @@ -125,6 +132,7 @@ sqlpp11 requires a certain api in order to connect with the database, see databa * MySQL: https://github.com/rbock/sqlpp11-connector-mysql * Sqlite3: https://github.com/rbock/sqlpp11-connector-sqlite3 * PostgreSQL: https://github.com/matthijs/sqlpp11-connector-postgresql + * ODBC: https://github.com/Erroneous1/sqlpp11-connector-odbc (experimental) To demonstrate that sqlpp11 can work with other backends as well, here is an experimental backend for structs in standard containers: @@ -159,3 +167,4 @@ Include generated header (MyTable.h), that's all License: ------------- sqlpp11 is distributed under the [BSD 2-Clause License](https://github.com/rbock/sqlpp11/blob/master/LICENSE). + diff --git a/cmake/Sqlpp11Config.cmake b/cmake/Sqlpp11Config.cmake new file mode 100644 index 00000000..033d7f4b --- /dev/null +++ b/cmake/Sqlpp11Config.cmake @@ -0,0 +1,26 @@ +# Copyright (c) 2016, Christian David +# 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("${CMAKE_CURRENT_LIST_DIR}/Sqlpp11Targets.cmake") diff --git a/include/sqlpp11/aggregate_functions/avg.h b/include/sqlpp11/aggregate_functions/avg.h index 4d274d46..f3c273b8 100644 --- a/include/sqlpp11/aggregate_functions/avg.h +++ b/include/sqlpp11/aggregate_functions/avg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, Roland Bock + * Copyright (c) 2013-2016, Roland Bock * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -111,7 +111,7 @@ namespace sqlpp { static_assert(not contains_aggregate_function_t>::value, "avg() cannot be used on an aggregate function"); - static_assert(is_numeric_t>::value, "avg() requires a value expression as argument"); + static_assert(is_numeric_t>::value, "avg() requires a numeric value expression as argument"); return {t}; } @@ -120,7 +120,7 @@ namespace sqlpp { static_assert(not contains_aggregate_function_t>::value, "avg() cannot be used on an aggregate function"); - static_assert(is_numeric_t>::value, "avg() requires a value expression as argument"); + static_assert(is_numeric_t>::value, "avg() requires a numeric value expression as argument"); return {t}; } } diff --git a/include/sqlpp11/bad_statement.h b/include/sqlpp11/bad_statement.h index 03635a28..c8b78a5c 100644 --- a/include/sqlpp11/bad_statement.h +++ b/include/sqlpp11/bad_statement.h @@ -31,6 +31,8 @@ namespace sqlpp { struct bad_statement { + static constexpr bool value = false; + template bad_statement(T&&...) { diff --git a/include/sqlpp11/basic_expression_operators.h b/include/sqlpp11/basic_expression_operators.h index 23dd5b6c..352ea394 100644 --- a/include/sqlpp11/basic_expression_operators.h +++ b/include/sqlpp11/basic_expression_operators.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, Roland Bock + * Copyright (c) 2013-2016, Roland Bock * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -28,7 +28,7 @@ #define SQLPP_DETAIL_BASIC_EXPRESSION_OPERATORS_H #include -#include +#include #include #include #include @@ -41,206 +41,171 @@ namespace sqlpp { - SQLPP_PORTABLE_STATIC_ASSERT(assert_valid_rhs_comparison_operand_t, "invalid rhs operand in comparison"); + SQLPP_PORTABLE_STATIC_ASSERT(assert_comparison_rhs_is_expression_t, "rhs operand in comparison is not an expression"); + SQLPP_PORTABLE_STATIC_ASSERT(assert_comparison_rhs_is_valid_operand_t, "invalid rhs operand in comparison"); + SQLPP_PORTABLE_STATIC_ASSERT(assert_comparison_lhs_rhs_differ_t, "identical lhs and rhs operands in comparison"); - template - using check_rhs_comparison_operand_t = - static_check_t<(is_expression_t>::value // expressions are OK - or - is_multi_expression_t>::value) // multi-expressions like ANY are - // OK for comparisons, too - and - LhsValueType::template _is_valid_operand< - sqlpp::wrap_operand_t>::value, // the correct value type is required, of course - assert_valid_rhs_comparison_operand_t>; + template + using check_comparison_impl = static_combined_check_t< + static_check_t::value, is_multi_expression_t::value>::value, + assert_comparison_rhs_is_expression_t>, + static_check_t::template _is_valid_operand::value, + assert_comparison_rhs_is_valid_operand_t>, + static_check_t::value, assert_comparison_lhs_rhs_differ_t>>; - SQLPP_PORTABLE_STATIC_ASSERT(assert_valid_in_arguments_t, "at least one operand of in() is not valid"); + template + using check_comparison_t = check_comparison_impl>; - template - using check_rhs_in_arguments_t = - static_check_t::value...>::value, - assert_valid_in_arguments_t>; + template + using check_in_impl = static_combined_check_t< + static_check_t::value...>::value, assert_comparison_rhs_is_expression_t>, + static_check_t::template _is_valid_operand::value...>::value, + assert_comparison_rhs_is_valid_operand_t>, + static_check_t::value...>::value, + assert_comparison_lhs_rhs_differ_t>>; - namespace detail - { - template class Expr, typename Lhs> - struct new_unary_expression_impl - { - using type = bad_statement; - }; - - template