diff --git a/.travis.yml b/.travis.yml index bf1cc60f9..d72f789fd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,81 +1,114 @@ +# Defaults +os: linux +dist: xenial -language: cpp -sudo: required +matrix: + include: + - name: "GCC-9" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-9 + - ninja-build + env: + - CC: gcc-9 + - CXX: g++-9 -compiler: - - clang + - name: "GCC-7" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-7 + - ninja-build + env: + - CC: gcc-7 + - CXX: g++-7 -os: - - osx + - name: "LLVM/Clang (Travis default)" + language: cpp + compiler: clang + addons: + apt: + packages: + - ninja-build + env: + - SQLITE_ORM_OMITS_CODECVT: ON + - name: AppleClang-10.0.1 + os: osx + osx_image: xcode10.2 + language: cpp + env: + - SQLITE_ORM_OMITS_CODECVT: ON + addons: + homebrew: + packages: + - catch2 + - ninja + update: true + + - name: "LLVM/Clang (latest)" + os: osx + osx_image: xcode10.2 + addons: + homebrew: + packages: + - llvm + - catch2 + - ninja + update: true + env: + - CPPFLAGS: "-I/usr/local/opt/llvm/include" + - LDFLAGS: "-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib" + - CPATH: /usr/local/opt/llvm/include + - LIBRARY_PATH: /usr/local/opt/llvm/lib + - LD_LIBRARY_PATH: /usr/local/opt/llvm/lib + - CC: /usr/local/opt/llvm/bin/clang + - CXX: /usr/local/opt/llvm/bin/clang++ + - SQLITE_ORM_OMITS_CODECVT: ON + + - name: "GCC-6" + os: osx + osx_image: xcode10.2 + addons: + homebrew: + packages: + - gcc@6 + - catch2 + - ninja + update: true + env: + - CC: gcc-6 + - CXX: g++-6 + +before_install: + - | + if [[ ${TRAVIS_OS_NAME} == "osx" ]]; then + export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH" # Use coreutils from homebrew. + fi + +install: + - | + # Catch2 test framework + if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then + git clone --depth=1 --quiet https://github.com/catchorg/Catch2.git + cd Catch2 + cmake -Bbuild -H. -DBUILD_TESTING=OFF + sudo env "PATH=$PATH" cmake --build ./build --target install + fi + +# scripts to run before build +before_script: + - if [[ "$CXX" == *"clang"* ]]; then clang --version ; fi + - cd ${TRAVIS_BUILD_DIR} + - mkdir compile && cd compile + - cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DSQLITE_ORM_OMITS_CODECVT="${SQLITE_ORM_OMITS_CODECVT:OFF}" .. + +# build examples, and run tests (ie make & make test) script: - - wget https://sqlite.org/2017/sqlite-amalgamation-3190300.zip - - unzip sqlite-amalgamation-3190300.zip - - mkdir sqlite_amalgamation - - cp -r sqlite-amalgamation-3190300/* sqlite_amalgamation - - rm sqlite-amalgamation-3190300.zip - - clang -c sqlite-amalgamation-3190300/sqlite3.c -o sqlite.static - - clang++ -std=c++1y tests/tests.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -stdlib=libc++ -o tests.out - - ./tests.out - - clang++ -std=c++1y tests/tests.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -stdlib=libc++ -D SQLITE_ORM_OMITS_CODECVT -o tests_without_codecvt.out - - ./tests_without_codecvt.out - - clang++ -std=c++1y tests/static_tests.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -stdlib=libc++ -o static_tests.out - - ./static_tests.out - - clang++ -std=c++1y examples/core_functions.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/distinct.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/enum_binding.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/group_by.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/in_memory.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/iteration.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/key_value.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/nullable_enum_binding.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/select.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/subentities.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/insert.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/update.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/multi_table_select.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/cross_join.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/blob.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/foreign_key.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/index.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/date_time.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/composite_key.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/unique.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/synchronous.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/self_join.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/natural_join.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/union.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/subquery.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/having.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/exists.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/except_intersection.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out - - clang++ -std=c++1y examples/custom_aliases.cpp sqlite.static -I include/ -I sqlite_amalgamation/ -ldl -lpthread -o a.out - - ./a.out + - cmake --build . --config Debug -- -k 10 + - ctest --verbose --output-on-failure -C Debug -j $(nproc) diff --git a/CMakeLists.txt b/CMakeLists.txt index c6065e890..ae4832b78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,6 @@ cmake_minimum_required (VERSION 3.2) +cmake_policy(SET CMP0057 NEW) + set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -10,6 +12,8 @@ set(PACKAGE_VERSION ${sqlite_orm_VERSION}) project("sqlite_orm" VERSION ${PACKAGE_VERSION}) +set(CMAKE_VERBOSE_MAKEFILE ON) + message(STATUS "Configuring ${CMAKE_PROJECT_NAME} ${sqlite_orm_VERSION}") set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/build/cmake") diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..7869bd2ac --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,94 @@ +# How to Contribute # + +Thank you for your interest in contributing to the sqlite_orm project! + +## GitHub pull requests ## + +This is the preferred method of submitting changes. When you submit a pull request through github, +it activates the continuous integration (CI) build systems at Appveyor and Travis to build your changes +on a variety of Linux, Windows and MacOS configurations and run all the test suites. Follow these requirements +for a successful pull request: + + 1. All significant changes require a [github issue](https://github.com/fnc12/sqlite_orm/issues). Trivial changes such as fixing a typo or a compiler warning do not. + + 1. The pull request title must begin with the github issue identifier if it has an associated issue, for example: + + #9999 : an example pull request title + + 1. Commit messages must be understandable in future by different developers and must be written in english language only: + +Instructions: + + 1. Create a fork in your GitHub account of http://github.com/fnc12/sqlite_orm + 1. Clone the fork to your development system. + 1. Create a branch for your changes (best practice is following git flow pattern with issue number as branch name, e.g. feature/9999-some-feature or bugfix/9999-some-bug). + 1. Modify the source to include the improvement/bugfix, and: + + * Remember to provide *tests* for all submitted changes! + * Use test-driven development (TDD): add a test that will isolate the bug *before* applying the change that fixes it. + * Verify that you follow current code style on sqlite_orm. + * [*optional*] Verify that your change works on other platforms by adding a GitHub service hook to [Travis CI](http://docs.travis-ci.com/user/getting-started/#Step-one%3A-Sign-in) and [AppVeyor](http://www.appveyor.com/docs). You can use this technique to run the sqlite_orm CI jobs in your account to check your changes before they are made public. Every GitHub pull request into sqlite_orm will run the full CI build and test suite on your changes. + + 1. Commit and push changes to your branch (please use issue name and description as commit title, e.g. "make it perfect. (fixes #9999)"). + 1. Use GitHub to create a pull request going from your branch to sqlite_orm:dev. Ensure that the github issue number is at the beginning of the title of your pull request. + 1. Wait for other contributors or committers to review your new addition, and for a CI build to complete. + 1. Wait for a owner or collaborators to commit your patch. + +## If you want to build the project locally ## + +See our detailed instructions on the [CMake README](/build/cmake/README.md). + +## If you want to review open issues... ## + + 1. Review the [GitHub Pull Request Backlog](https://github.com/fnc12/sqlite_orm/pulls). Code reviews are opened to all. + +## If you discovered a defect... ## + + 1. Check to see if the issue is already in the [github issues](https://github.com/fnc12/sqlite_orm/issues). + 1. If not please create an issue describing the change you're proposing in the github issues page. + 1. Contribute your code changes using the GitHub pull request method: + +## GitHub recipes for Pull Requests ## + +Sometimes commmitters may ask you to take actions in your pull requests. Here are some recipes that will help you accomplish those requests. These examples assume you are working on github issue 9999. You should also be familiar with the [upstream](https://help.github.com/articles/syncing-a-fork/) repository concept. + +### Squash your changes ### + +If you have commits with adding code which is removed in a different commit within the same PR then please squash all commits to remove unnecessary add commits. + +1. Use the command ``git log`` to identify how many commits you made since you began. +2. Use the command ``git rebase -i HEAD~N`` where N is the number of commits. +3. Leave "pull" in the first line. +4. Change all other lines from "pull" to "fixup". +5. All your changes are now in a single commit. + +If you already have a pull request outstanding, you will need to do a "force push" to overwrite it since you changed your commit history: + + git push -u origin feature/9999-make-perfect --force + +A more detailed walkthrough of a squash can be found at [Git Ready](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html). + +### Rebase your pull request ### + +If your pull request has a conflict with dev, it needs to be rebased: + + git checkout feature/9999-make-perfect + git rebase upstream dev + (resolve any conflicts, make sure it builds) + git push -u origin feature/9999-make-perfect --force + +### Fix a bad merge ### + +If your pull request contains commits that are not yours, then you should use the following technique to fix the bad merge in your branch: + + git checkout dev + git pull upstream dev + git checkout -b feature/9999-make-perfect-take-2 + git cherry-pick ... + (pick only your commits from your original pull request in ascending chronological order) + squash your changes to a single commit if there is more than one (see above) + git push -u origin feature/9999-make-perfect-take-2:feature/9999-make-perfect + +This procedure will apply only your commits in order to the current dev, then you will squash them to a single commit, and then you force push your local feature/9999-make-perfect-take-2 into remote feature/9999-make-perfect which represents your pull request, replacing all the commits with the new one. + + diff --git a/README.md b/README.md index 85af0b387..212d81c73 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,10 @@ SQLite ORM light header only library for modern C++ # Status -| Branch | Travis | Appveyor | Coverity Scan | codecov.io | Website | -| :----- | :----- | :------- | :------------ | :--------- | :------ | -| [`master`](https://github.com/fcn12/sqlite_orm/tree/master) | [![Build Status](https://travis-ci.org/fnc12/sqlite_orm.svg?branch=master)](https://travis-ci.org/fcn12/sqlite_orm) | [![Build status](https://ci.appveyor.com/api/projects/status/github/fnc12/sqlite_orm?branch=master&svg=true)](https://ci.appveyor.com/project/fnc12/sqlite-orm/history) | | | [![Website](https://img.shields.io/badge/official-website-brightgreen.svg)](https://github.com/fcn12/sqlite_orm/) | -| [`dev`](https://github.com/fcn12/sqlite_orm/tree/dev) | [![Build Status](https://travis-ci.org/fnc12/sqlite_orm.svg?branch=dev)](https://travis-ci.org/fcn12/sqlite_orm) | [![Build status](https://ci.appveyor.com/api/projects/status/github/fnc12/sqlite_orm?branch=dev&svg=true)](https://ci.appveyor.com/project/fnc12/sqlite-orm/history) | | | [![Website](https://img.shields.io/badge/official-website-brightgreen.svg)](https://github.com/fcn12/sqlite_orm/tree/dev) | +| Branch | Travis | Appveyor | +| :----- | :----- | :------- | +| [`master`](https://github.com/fnc12/sqlite_orm/tree/master) | [![Build Status](https://travis-ci.org/fnc12/sqlite_orm.svg?branch=master)](https://travis-ci.org/fnc12/sqlite_orm) | [![Build status](https://ci.appveyor.com/api/projects/status/github/fnc12/sqlite_orm?branch=master&svg=true)](https://ci.appveyor.com/project/fnc12/sqlite-orm/history) | | | [![Website](https://img.shields.io/badge/official-website-brightgreen.svg)](https://github.com/fnc12/sqlite_orm/) | +| [`dev`](https://github.com/fnc12/sqlite_orm/tree/dev) | [![Build Status](https://travis-ci.org/fnc12/sqlite_orm.svg?branch=dev)](https://travis-ci.org/fnc12/sqlite_orm) | [![Build status](https://ci.appveyor.com/api/projects/status/github/fnc12/sqlite_orm?branch=dev&svg=true)](https://ci.appveyor.com/project/fnc12/sqlite-orm/history) | | | [![Website](https://img.shields.io/badge/official-website-brightgreen.svg)](https://github.com/fnc12/sqlite_orm/tree/dev) | # Advantages @@ -46,7 +46,7 @@ SQLite ORM light header only library for modern C++ * **COLLATE support** * **Limits setting/getting support** -`sqlite_orm` library allows to create easy data model mappings to your database schema. It is built to manage (CRUD) objects with a single column with primary key and without it. It also allows you to specify table names and column names explicitly no matter how your classes actually named. Take a look at example: +`sqlite_orm` library allows to create easy data model mappings to your database schema. It is built to manage (CRUD) objects with a primary key and without it. It also allows you to specify table names and column names explicitly no matter how your classes actually named. Take a look at example: ```c++ @@ -72,7 +72,7 @@ So we have database with predefined schema like `CREATE TABLE user_types (id integer primary key autoincrement, name text not null DEFAULT 'name_placeholder')` -Now we tell `sqlite_orm` library about schema and provide database filename. We create `storage` service object that has CRUD interface. Also we create every table and every column. All code is intuitive and minimalistic. +Now we tell `sqlite_orm` library about our schema and provide database filename. We create `storage` service object that has CRUD interface. Also we create every table and every column. All code is intuitive and minimalistic. ```c++ @@ -90,15 +90,15 @@ auto storage = make_storage("db.sqlite", make_column("name", &UserType::name, default_value("name_placeholder")))); ``` -Too easy isn't it? You do not have to specify mapped type explicitly - it is deduced from your member pointers you pass during making a column (for example: `&User::id`). To create a column you have to pass two arguments at least: its name in the table and your mapped class member pointer. You can also add extra arguments to tell your storage about column's constraints like ~~`not_null`~~ (deduced from type), `primary_key`, `autoincrement`, `default_value` or `unique`(order isn't important). +Too easy isn't it? You do not have to specify mapped type explicitly - it is deduced from your member pointers you pass during making a column (for example: `&User::id`). To create a column you have to pass two arguments at least: its name in the table and your mapped class member pointer. You can also add extra arguments to tell your storage about column's constraints like `primary_key`, `autoincrement`, `default_value` or `unique`(order isn't important; `not_null` is deduced from type automatically). -If your datamodel classes have private or protected members to map to sqlite then you can make a storage with setter and getter functions. More info in the [example](https://github.com/fnc12/sqlite_orm/blob/master/examples/private_class_members.cpp). +More details about making storage can be found in [tutorial](https://github.com/fnc12/sqlite_orm/wiki/Making-a-storage). -More details about making storage can be found in [tutorial](https://github.com/fnc12/sqlite_orm/wiki/Making-storage). +If your datamodel classes have private or protected members to map to sqlite then you can make a storage with setter and getter functions. More info in the [example](https://github.com/fnc12/sqlite_orm/blob/master/examples/private_class_members.cpp). # CRUD -Let's create and insert new `User` into database. First we need to create a `User` object with any id and call `insert` function. It will return id of just created user or throw exception if something goes wrong. +Let's create and insert new `User` into our database. First we need to create a `User` object with any id and call `insert` function. It will return id of just created user or throw exception if something goes wrong. ```c++ User user{-1, "Jonh", "Doe", 664416000, std::make_unique("url_to_heaven"), 3 }; @@ -113,6 +113,8 @@ secondUser.id = insertedId; ``` +Note: if we need to insert a new user with specified id call `storage.replace(user);` instead of `insert`. + Next let's get our user by id. ```c++ @@ -377,7 +379,7 @@ for(auto &user : whereNameLike) { } ``` -Looks like magic but it works very simple. Cute function `c` (column) takes a class pointer and returns a special expression middle object that can be used with operators overloaded in `::sqlite_orm` namespace. Operator overloads act just like functions +Looks like magic but it works very simple. Cute function `c` (column) takes a class member pointer and returns a special expression middle object that can be used with operators overloaded in `::sqlite_orm` namespace. Operator overloads act just like functions * is_equal * is_not_equal @@ -388,7 +390,7 @@ Looks like magic but it works very simple. Cute function `c` (column) takes a cl * is_null * is_not_null -that simulate binary comparison operator so they take 2 arguments: left hand side and right hand side. Arguments may be either member pointer of mapped class or any other expression (core function or literal). Binary comparison functions map arguments to text to be passed to sqlite engine to process query. Member pointers are being mapped to column names and literals to literals (numbers to raw numbers and string to quoted strings). Next `where` function places brackets around condition and adds "WHERE" keyword before condition text. Next resulted string appends to query string and is being processed further. +that simulate binary comparison operator so they take 2 arguments: left hand side and right hand side. Arguments may be either member pointer of mapped class or any other expression (core/aggregate function, literal or subexpression). Binary comparison functions map arguments to text to be passed to sqlite engine to process query. Member pointers are being mapped to column names and literals/variables/constants to '?' and then are bound automatically. Next `where` function places brackets around condition and adds "WHERE" keyword before condition text. Next resulted string appends to a query string and is being processed further. If you omit `where` function in `get_all` it will return all objects from a table: @@ -672,7 +674,7 @@ storage.transaction([&] () mutable { It will print a number of deleted users (rows). But if you call `changes` without a transaction and your database is located in file not in RAM the result will be 0 always cause `sqlite_orm` opens and closes connection every time you call a function without a transaction. -Also a `transaction` function returns `true` if transaction is commited and `false` if it is rollbacked. It can be useful if your next moves depend on transaction result: +Also a `transaction` function returns `true` if transaction is commited and `false` if it is rollbacked. It can be useful if your next actions depend on transaction result: ```c++ auto commited = storage.transaction([&] () mutable { @@ -730,7 +732,29 @@ For more details please check the project [wiki](https://github.com/fnc12/sqlite # Installation -Just put `include/sqlite_orm/sqlite_orm.h` into you folder with headers. Also it is recommended to keep project libraries' sources in separate folders cause there is no normal dependency manager for C++ yet. +Use popular package manager like [vcpkg](https://github.com/Microsoft/vcpkg) and just install it with `vcpkg install sqlite_orm` command. + +Or you can use below instructions + +```bash +git clone https://github.com/fnc12/sqlite_orm.git sqlite_orm +cd sqlite_orm +mkdir compile +cd compile +cmake .. +cmake --build . +sudo make install +``` + +then you can just include `sqlite_orm.h` that is installed in system-wide header files location or in case you use cmake build system you can just add below commands in CMakeLists.txt + +```cmake +find_package(sqlite_orm CONFIG REQUIRED) +target_link_libraries(main PRIVATE sqlite_orm::sqlite_orm) +target_include_directories(main PRIVATE ${SQLITE_ORM_INCLUDE_DIR}) +``` + +Or just put `include/sqlite_orm/sqlite_orm.h` into you folder with headers. Also it is recommended to keep project libraries' sources in separate folders cause there is no dominant normal dependency manager for C++ yet. # Requirements diff --git a/TODO.md b/TODO.md index c2b81c7d7..b86065dc5 100644 --- a/TODO.md +++ b/TODO.md @@ -4,21 +4,18 @@ * `FOREIGN KEY` - sync_schema fk comparison and ability of two tables to have fk to each other * rest of core functions(https://sqlite.org/lang_corefunc.html) -* `CASE` -* asterisk in raw select: `SELECT rowid, * FROM table` * `ATTACH` * blob incremental I/O https://sqlite.org/c3ref/blob_open.html * reusing of prepared statements - useful for query optimisation * explicit FROM for subqueries in FROM argument * backup API https://www.sqlite.org/backup.html * busy handler https://sqlite.org/c3ref/busy_handler.html -* CAST https://sqlite.org/lang_expr.html#castexpr * CREATE VIEW and other view operations https://sqlite.org/lang_createview.html * triggers * query static check for correct order (e.g. `GROUP BY` after `WHERE`) -* column alias -* `FULL OUTER JOIN` * `WINDOW` * `UPSERT` https://www.sqlite.org/lang_UPSERT.html +* `CHECK` constraint +* `SAVEPOINT` https://www.sqlite.org/lang_savepoint.html Please feel free to add any feature that isn't listed here and not implemented yet. diff --git a/appveyor.yml b/appveyor.yml old mode 100755 new mode 100644 index 9a6fcc924..5a50b9e19 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,40 +1,48 @@ -# Copyright (c) 2012-2019 Sebastien Rombauts (sebastien.rombauts@gmail.com) - # build format version: "{build}" -# scripts that run after cloning repository -install: - - git submodule update --init --recursive - +skip_branch_with_pr: true +skip_commits: + files: + - .git* + - .travis.yml + - _config.yml + - LICENSE + - '*.md' + - '*.png' + - '*.sh' + image: - Visual Studio 2017 - + # configurations to add to build matrix # TODO: MinGW Makefiles and MSYS Makefiles configuration: - Debug - Release -environment: - matrix: - - arch: Win32 - - arch: Win64 +platform: + - x86 + - x64 init: - - echo %APPVEYOR_BUILD_WORKER_IMAGE% - %configuration% - %arch% - - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (set vs=Visual Studio 15 2017) - - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" (set vs=Visual Studio 14 2015) - - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" (set vs=Visual Studio 12 2013) - - if "%arch%"=="Win64" (set generator="%vs% Win64") - - if "%arch%"=="Win32" (set generator="%vs%") - - echo %generator% - + - echo %APPVEYOR_BUILD_WORKER_IMAGE% - %configuration% - %PLATFORM% + - if "%PLATFORM%"=="x64" (set architecture=-A x64) + - if "%PLATFORM%"=="x86" (set architecture=-A Win32) + - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" (set generator="Visual Studio 16 2019" %architecture%) + - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (set generator="Visual Studio 15 2017" %architecture%) + - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" (set generator="Visual Studio 14 2015" %architecture%) + - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" (set generator="Visual Studio 12 2013" %architecture%) + +install: + - C:\Tools\vcpkg\vcpkg integrate install + - vcpkg install catch2:%PLATFORM%-windows + # scripts to run before build before_build: - mkdir compile - cd compile - - cmake -DSqliteOrm_BuildTests=ON .. -G %generator% + - cmake -DSqliteOrm_BuildTests=ON .. -G %generator% -DCMAKE_TOOLCHAIN_FILE=C:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake # build examples, and run tests (ie make & make test) build_script: diff --git a/build.sh b/build.sh new file mode 100755 index 000000000..5ca371f61 --- /dev/null +++ b/build.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +# exit on firts error +set -e + +echo "Prepare third party libraries" +cd third_party +git clone https://github.com/Microsoft/vcpkg.git vcpkg +cd vcpkg +chmod +x bootstrap-vcpkg.sh + +if [[ "$CXX" == *"clang"* && "$TRAVIS_OS_NAME" == "osx" ]]; then ./bootstrap-vcpkg.sh --allowAppleClang ; else ./bootstrap-vcpkg.sh ; fi + +chmod +x vcpkg +./vcpkg install gtest +cd ../.. + +echo "Prepare for compile" +mkdir -p compile +cd compile + +cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../third_party/vcpkg/scripts/buildsystems/vcpkg.cmake .. + +echo "Compiling..." +cmake --build . --config Debug + +echo "Run testing..." +ctest -C Debug --output-on-failure -V + diff --git a/dev/ast_iterator.h b/dev/ast_iterator.h new file mode 100644 index 000000000..be5e9b60c --- /dev/null +++ b/dev/ast_iterator.h @@ -0,0 +1,320 @@ +#pragma once + +#include // std::vector + +#include "conditions.h" +#include "select_constraints.h" +#include "operators.h" +#include "tuple_helper.h" +#include "core_functions.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * ast_iterator accepts an any expression and a callable object + * which will be called for any node of provided expression. + * E.g. if we pass `where(is_equal(5, max(&User::id, 10))` then + * callable object will be called with 5, &User::id and 10. + * ast_iterator is used mostly in finding literals to be bound to + * a statement. To use it just call `iterate_ast(object, callable);` + * T is an ast element. E.g. where_t + */ + template + struct ast_iterator { + using node_type = T; + + /** + * L is a callable type. Mostly is templated lambda + */ + template + void operator()(const T &t, const L &l) const { + l(t); + } + }; + + /** + * Simplified API + */ + template + void iterate_ast(const T &t, const L &l) { + ast_iterator iterator; + iterator(t, l); + } + + template + struct ast_iterator, void> { + using node_type = conditions::where_t; + + template + void operator()(const node_type &where, const L &l) const { + iterate_ast(where.c, l); + } + }; + + template + struct ast_iterator::value>::type> { + using node_type = T; + + template + void operator()(const node_type &binaryCondition, const L &l) const { + iterate_ast(binaryCondition.l, l); + iterate_ast(binaryCondition.r, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = binary_operator; + + template + void operator()(const node_type &binaryOperator, const C &l) const { + iterate_ast(binaryOperator.lhs, l); + iterate_ast(binaryOperator.rhs, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = columns_t; + + template + void operator()(const node_type &cols, const L &l) const { + iterate_ast(cols.columns, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = conditions::in_t; + + template + void operator()(const node_type &in, const C &l) const { + iterate_ast(in.l, l); + iterate_ast(in.arg, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = std::vector; + + template + void operator()(const node_type &vec, const L &l) const { + for(auto &i : vec) { + iterate_ast(i, l); + } + } + }; + + template<> + struct ast_iterator, void> { + using node_type = std::vector; + + template + void operator()(const node_type &vec, const L &l) const { + l(vec); + } + }; + + template + struct ast_iterator::value>::type> { + using node_type = T; + + template + void operator()(const node_type &c, const L &l) const { + iterate_ast(c.left, l); + iterate_ast(c.right, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = select_t; + + template + void operator()(const node_type &sel, const L &l) const { + iterate_ast(sel.col, l); + iterate_ast(sel.conditions, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = std::tuple; + + template + void operator()(const node_type &tuple, const L &l) const { + iterate_tuple(tuple, [&l](auto &v){ + iterate_ast(v, l); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = conditions::having_t; + + template + void operator()(const node_type &hav, const L &l) const { + iterate_ast(hav.t, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = conditions::cast_t; + + template + void operator()(const node_type &c, const L &l) const { + iterate_ast(c.expression, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = conditions::exists_t; + + template + void operator()(const node_type &e, const L &l) const { + iterate_ast(e.t, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = conditions::like_t; + + template + void operator()(const node_type &lk, const L &l) const { + iterate_ast(lk.arg, l); + iterate_ast(lk.pattern, l); + lk.arg3.apply([&l](auto &value){ + iterate_ast(value, l); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = conditions::between_t; + + template + void operator()(const node_type &b, const L &l) const { + iterate_ast(b.expr, l); + iterate_ast(b.b1, l); + iterate_ast(b.b2, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = conditions::named_collate; + + template + void operator()(const node_type &col, const L &l) const { + iterate_ast(col.expr, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = conditions::negated_condition_t; + + template + void operator()(const node_type &neg, const L &l) const { + iterate_ast(neg.c, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = core_functions::core_function_t; + + template + void operator()(const node_type &f, const L &l) const { + iterate_ast(f.args, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = conditions::left_join_t; + + template + void operator()(const node_type &j, const L &l) const { + iterate_ast(j.constraint, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = conditions::on_t; + + template + void operator()(const node_type &o, const L &l) const { + iterate_ast(o.arg, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = conditions::join_t; + + template + void operator()(const node_type &j, const L &l) const { + iterate_ast(j.constraint, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = conditions::left_outer_join_t; + + template + void operator()(const node_type &j, const L &l) const { + iterate_ast(j.constraint, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = conditions::inner_join_t; + + template + void operator()(const node_type &j, const L &l) const { + iterate_ast(j.constraint, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = simple_case_t; + + template + void operator()(const node_type &c, const L &l) const { + c.case_expression.apply([&l](auto &c){ + iterate_ast(c, l); + }); + iterate_tuple(c.args, [&l](auto &pair){ + iterate_ast(pair.first, l); + iterate_ast(pair.second, l); + }); + c.else_expression.apply([&l](auto &el){ + iterate_ast(el, l); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = as_t; + + template + void operator()(const node_type &a, const L &l) const { + iterate_ast(a.expression, l); + } + }; + } +} diff --git a/dev/column.h b/dev/column.h index 6e1fcf1b4..e5a3c9f62 100644 --- a/dev/column.h +++ b/dev/column.h @@ -3,7 +3,7 @@ #include // std::tuple #include // std::string #include // std::unique_ptr -#include // std::true_type, std::false_type, std::is_same, std::enable_if +#include // std::true_type, std::false_type, std::is_same, std::enable_if, std::is_member_pointer, std::is_member_function_pointer #include "type_is_nullable.h" #include "tuple_helper.h" @@ -14,6 +14,14 @@ namespace sqlite_orm { namespace internal { + struct column_base { + + /** + * Column name. Specified during construction in `make_column`. + */ + const std::string name; + }; + /** * This class stores single column info. column_t is a pair of [column_name:member_pointer] mapped to a storage * O is a mapped class, e.g. User @@ -21,7 +29,7 @@ namespace sqlite_orm { * Op... is a constraints pack, e.g. primary_key_t, autoincrement_t etc */ template - struct column_t { + struct column_t : column_base { using object_type = O; using field_type = T; using constraints_type = std::tuple; @@ -29,11 +37,6 @@ namespace sqlite_orm { using getter_type = G; using setter_type = S; - /** - * Column name. Specified during construction in `make_column`. - */ - const std::string name; - /** * Member pointer used to read/write member */ @@ -55,6 +58,10 @@ namespace sqlite_orm { */ constraints_type constraints; + column_t(std::string name, member_pointer_t member_pointer_, getter_type getter_, setter_type setter_, constraints_type constraints_): + column_base{std::move(name)}, member_pointer(member_pointer_), getter(getter_), setter(setter_), constraints(std::move(constraints_)) + {} + /** * Simplified interface for `NOT NULL` constraint */ @@ -85,9 +92,9 @@ namespace sqlite_orm { * Simplified interface for `DEFAULT` constraint * @return string representation of default value if it exists otherwise nullptr */ - std::unique_ptr default_value() { + std::unique_ptr default_value() const { std::unique_ptr res; - tuple_helper::iterator::value - 1, Op...>()(constraints, [&res](auto &v){ + iterate_tuple(this->constraints, [&res](auto &v){ auto dft = internal::default_value_extractor()(v); if(dft){ res = std::move(dft); @@ -109,6 +116,15 @@ namespace sqlite_orm { template struct is_column> : public std::true_type {}; + template + struct is_field_member_pointer : std::false_type {}; + + template + struct is_field_member_pointer::value && !std::is_member_function_pointer::value>::type> : std::true_type {}; + + /** + * Getters aliases + */ template using getter_by_value_const = T (O::*)() const; @@ -127,6 +143,9 @@ namespace sqlite_orm { template using getter_by_const_ref = const T& (O::*)(); + /** + * Setters aliases + */ template using setter_by_value = void (O::*)(T); diff --git a/dev/column_result.h b/dev/column_result.h index 047d46844..a67c3bfe8 100644 --- a/dev/column_result.h +++ b/dev/column_result.h @@ -50,86 +50,8 @@ namespace sqlite_orm { }; template - struct column_result_t, void> { - using type = int; - }; - -#if SQLITE_VERSION_NUMBER >= 3007016 - - template - struct column_result_t, void> { - using type = std::string; - }; -#endif - - template - struct column_result_t { - using type = int; - }; - - template - struct column_result_t { - using type = int; - }; - - template - struct column_result_t, void> { - using type = std::unique_ptr; - }; - - template - struct column_result_t, void> { - using type = std::string; - }; - - template - struct column_result_t, void> { - using type = std::string; - }; - - template - struct column_result_t, void> { - using type = std::string; - }; - - template - struct column_result_t, void> { - using type = std::string; - }; - - template - struct column_result_t, void> { - using type = std::string; - }; - - template - struct column_result_t, void> { - using type = std::string; - }; - - template - struct column_result_t, void> { - using type = std::string; - }; - - template - struct column_result_t, void> { - using type = std::string; - }; - - template - struct column_result_t, void> { - using type = std::string; - }; - - template - struct column_result_t, void> { - using type = double; - }; - - template - struct column_result_t, void> { - using type = std::string; + struct column_result_t::value>::type> { + using type = typename T::return_type; }; template @@ -306,5 +228,15 @@ namespace sqlite_orm { struct column_result_t, void> { using type = T; }; + + template + struct column_result_t, void> { + using type = R; + }; + + template + struct column_result_t, void> { + using type = bool; + }; } } diff --git a/dev/conditions.h b/dev/conditions.h index bf550a0b9..f3b45aecb 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -1,9 +1,12 @@ #pragma once #include // std::string +#include // std::enable_if, std::is_same +#include // std::vector #include "collate_argument.h" #include "constraints.h" +#include "optional_container.h" namespace sqlite_orm { @@ -63,20 +66,27 @@ namespace sqlite_orm { } }; + struct named_collate_base { + std::string name; + + operator std::string () const { + return "COLLATE " + this->name; + } + }; + /** * Collated something with custom collate function */ template - struct named_collate { + struct named_collate : named_collate_base { T expr; - std::string name; - - named_collate() = default; - - named_collate(T expr_, std::string name_): expr(expr_), name(std::move(name_)) {} + named_collate(T expr_, std::string name_): named_collate_base{std::move(name_)}, expr(std::move(expr_)) {} + }; + + struct negated_condition_string { operator std::string () const { - return "COLLATE " + this->name; + return "NOT"; } }; @@ -84,78 +94,72 @@ namespace sqlite_orm { * Result of not operator */ template - struct negated_condition_t : public condition_t { + struct negated_condition_t : condition_t, negated_condition_string { C c; - negated_condition_t() = default; - - negated_condition_t(C c_): c(c_) {} - - operator std::string () const { - return "NOT"; - } + negated_condition_t(C c_): c(std::move(c_)) {} }; /** - * Result of and operator + * Base class for binary conditions */ template - struct and_condition_t : public condition_t { + struct binary_condition : public condition_t { L l; R r; - and_condition_t() = default; - - and_condition_t(L l_, R r_): l(l_), r(r_) {} + binary_condition() = default; + binary_condition(L l_, R r_): l(std::move(l_)), r(std::move(r_)) {} + }; + + struct and_condition_string { operator std::string () const { return "AND"; } }; /** - * Result of or operator + * Result of and operator */ template - struct or_condition_t : public condition_t { - L l; - R r; - - or_condition_t() = default; - - or_condition_t(L l_, R r_): l(l_), r(r_) {} + struct and_condition_t : binary_condition, and_condition_string { + using super = binary_condition; + using super::super; + }; + + struct or_condition_string { operator std::string () const { return "OR"; } }; /** - * Base class for binary conditions + * Result of or operator */ template - struct binary_condition : public condition_t { - L l; - R r; + struct or_condition_t : binary_condition, or_condition_string { + using super = binary_condition; - binary_condition() = default; - - binary_condition(L l_, R r_): l(l_), r(r_) {} + using super::super; + }; + + struct is_equal_string { + operator std::string () const { + return "="; + } }; /** * = and == operators object */ template - struct is_equal_t : public binary_condition { + struct is_equal_t : binary_condition, is_equal_string { using self = is_equal_t; using binary_condition::binary_condition; - operator std::string () const { - return "="; - } - negated_condition_t operator!() const { return {*this}; } @@ -178,19 +182,21 @@ namespace sqlite_orm { }; + struct is_not_equal_string { + operator std::string () const { + return "!="; + } + }; + /** * != operator object */ template - struct is_not_equal_t : public binary_condition { + struct is_not_equal_t : binary_condition, is_not_equal_string { using self = is_not_equal_t; using binary_condition::binary_condition; - operator std::string () const { - return "!="; - } - negated_condition_t operator!() const { return {*this}; } @@ -208,19 +214,21 @@ namespace sqlite_orm { } }; + struct greater_than_string { + operator std::string () const { + return ">"; + } + }; + /** * > operator object. */ template - struct greater_than_t : public binary_condition { + struct greater_than_t : binary_condition, greater_than_string { using self = greater_than_t; using binary_condition::binary_condition; - operator std::string () const { - return ">"; - } - negated_condition_t operator!() const { return {*this}; } @@ -238,19 +246,21 @@ namespace sqlite_orm { } }; + struct greater_or_equal_string { + operator std::string () const { + return ">="; + } + }; + /** * >= operator object. */ template - struct greater_or_equal_t : public binary_condition { + struct greater_or_equal_t : binary_condition, greater_or_equal_string { using self = greater_or_equal_t; using binary_condition::binary_condition; - operator std::string () const { - return ">="; - } - negated_condition_t operator!() const { return {*this}; } @@ -268,19 +278,21 @@ namespace sqlite_orm { } }; + struct lesser_than_string { + operator std::string () const { + return "<"; + } + }; + /** * < operator object. */ template - struct lesser_than_t : public binary_condition { + struct lesser_than_t : binary_condition, lesser_than_string { using self = lesser_than_t; using binary_condition::binary_condition; - operator std::string () const { - return "<"; - } - negated_condition_t operator!() const { return {*this}; } @@ -298,19 +310,21 @@ namespace sqlite_orm { } }; + struct lesser_or_equal_string { + operator std::string () const { + return "<="; + } + }; + /** * <= operator object. */ template - struct lesser_or_equal_t : public binary_condition { + struct lesser_or_equal_t : binary_condition, lesser_or_equal_string { using self = lesser_or_equal_t; using binary_condition::binary_condition; - operator std::string () const { - return "<="; - } - negated_condition_t> operator!() const { return {*this}; } @@ -328,31 +342,38 @@ namespace sqlite_orm { } }; + struct in_base { + bool negative = false; // used in not_in + + operator std::string () const { + if(!this->negative){ + return "IN"; + }else{ + return "NOT IN"; + } + } + }; + /** * IN operator object. */ template - struct in_t : public condition_t { + struct in_t : condition_t, in_base { using self = in_t; L l; // left expression A arg; // in arg - bool negative = false; // used in not_in - - in_t() = default; - in_t(L l_, A arg_, bool negative_): l(l_), arg(std::move(arg_)), negative(negative_) {} + in_t(L l_, A arg_, bool negative): in_base{negative}, l(l_), arg(std::move(arg_)) {} negated_condition_t operator!() const { return {*this}; } - + }; + + struct is_null_string { operator std::string () const { - if(!this->negative){ - return "IN"; - }else{ - return "NOT IN"; - } + return "IS NULL"; } }; @@ -360,16 +381,21 @@ namespace sqlite_orm { * IS NULL operator object. */ template - struct is_null_t { + struct is_null_t : is_null_string { using self = is_null_t; + T t; + is_null_t(T t_) : t(std::move(t_)) {} + negated_condition_t operator!() const { return {*this}; } - + }; + + struct is_not_null_string { operator std::string () const { - return "IS NULL"; + return "IS NOT NULL"; } }; @@ -377,29 +403,43 @@ namespace sqlite_orm { * IS NOT NULL operator object. */ template - struct is_not_null_t { + struct is_not_null_t : is_not_null_string { using self = is_not_null_t; T t; + is_not_null_t(T t_) : t(std::move(t_)) {} + negated_condition_t operator!() const { return {*this}; } - + }; + + struct where_string { operator std::string () const { - return "IS NOT NULL"; + return "WHERE"; } }; /** * WHERE argument holder. + * C is conditions type. Can be any condition like: is_equal_t, is_null_t, exists_t etc */ template - struct where_t { + struct where_t : where_string { C c; - operator std::string () const { - return "WHERE"; + where_t(C c_) : c(std::move(c_)) {} + }; + + struct order_by_base { + int asc_desc = 0; // 1: asc, -1: desc + std::string _collate_argument; + }; + + struct order_by_string { + operator std::string() const { + return "ORDER BY"; } }; @@ -407,20 +447,12 @@ namespace sqlite_orm { * ORDER BY argument holder. */ template - struct order_by_t { + struct order_by_t : order_by_base, order_by_string { using self = order_by_t; O o; - int asc_desc = 0; // 1: asc, -1: desc - std::string _collate_argument; - order_by_t(): o() {} - - order_by_t(O o_): o(o_) {} - - operator std::string() const { - return "ORDER BY"; - } + order_by_t(O o_): o(std::move(o_)) {} self asc() { auto res = *this; @@ -463,11 +495,60 @@ namespace sqlite_orm { * ORDER BY pack holder. */ template - struct multi_order_by_t { - std::tuple args; + struct multi_order_by_t : order_by_string { + using args_type = std::tuple; + + args_type args; + + multi_order_by_t(args_type &&args_) : args(std::move(args_)) {} + }; + + /** + * S - storage class + */ + template + struct dynamic_order_by_t : order_by_string { + using storage_type = S; + + struct entry_t : order_by_base { + std::string name; + + entry_t(decltype(name) name_, int asc_desc, std::string collate_argument) : + order_by_base{asc_desc, move(collate_argument)}, + name(move(name_)) + {} + }; + + using const_iterator = typename std::vector::const_iterator; + dynamic_order_by_t(const storage_type &storage_): storage(storage_) {} + + template + void push_back(order_by_t order_by) { + auto columnName = this->storage.string_from_expression(order_by.o, true); + entries.emplace_back(move(columnName), order_by.asc_desc, move(order_by._collate_argument)); + } + + const_iterator begin() const { + return this->entries.begin(); + } + + const_iterator end() const { + return this->entries.end(); + } + + void clear() { + this->entries.clear(); + } + + protected: + std::vector entries; + const storage_type &storage; + }; + + struct group_by_string { operator std::string() const { - return static_cast(order_by_t()); + return "GROUP BY"; } }; @@ -475,11 +556,16 @@ namespace sqlite_orm { * GROUP BY pack holder. */ template - struct group_by_t { - std::tuple args; + struct group_by_t : group_by_string { + using args_type = std::tuple; + args_type args; + group_by_t(args_type &&args_): args(std::move(args_)) {} + }; + + struct between_string { operator std::string() const { - return "GROUP BY"; + return "BETWEEN"; } }; @@ -487,34 +573,46 @@ namespace sqlite_orm { * BETWEEN operator object. */ template - struct between_t : public condition_t { + struct between_t : condition_t, between_string { A expr; T b1; T b2; - between_t() = default; - - between_t(A expr_, T b1_, T b2_): expr(expr_), b1(b1_), b2(b2_) {} - + between_t(A expr_, T b1_, T b2_): expr(std::move(expr_)), b1(std::move(b1_)), b2(std::move(b2_)) {} + }; + + struct like_string { operator std::string() const { - return "BETWEEN"; + return "LIKE"; } }; /** * LIKE operator object. */ - template - struct like_t : public condition_t { - A a; - T t; + template + struct like_t : condition_t, like_string { + using arg_t = A; + using pattern_t = T; + using escape_t = E; - like_t() = default; + arg_t arg; + pattern_t pattern; + internal::optional_container arg3; // not escape cause escape exists as a function here - like_t(A a_, T t_): a(a_), t(t_) {} + like_t(arg_t arg_, pattern_t pattern_, internal::optional_container escape): + arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape)) {} + template + like_t escape(C c) const { + internal::optional_container arg3{std::move(c)}; + return {std::move(this->arg), std::move(this->pattern), std::move(arg3)}; + } + }; + + struct cross_join_string { operator std::string() const { - return "LIKE"; + return "CROSS JOIN"; } }; @@ -523,11 +621,13 @@ namespace sqlite_orm { * T is joined type which represents any mapped table. */ template - struct cross_join_t { + struct cross_join_t : cross_join_string { using type = T; - + }; + + struct natural_join_string { operator std::string() const { - return "CROSS JOIN"; + return "NATURAL JOIN"; } }; @@ -536,11 +636,13 @@ namespace sqlite_orm { * T is joined type which represents any mapped table. */ template - struct natural_join_t { + struct natural_join_t : natural_join_string { using type = T; - + }; + + struct left_join_string { operator std::string() const { - return "NATURAL JOIN"; + return "LEFT JOIN"; } }; @@ -550,14 +652,18 @@ namespace sqlite_orm { * O is on(...) argument type. */ template - struct left_join_t { + struct left_join_t : left_join_string { using type = T; using on_type = O; on_type constraint; + left_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct join_string { operator std::string() const { - return "LEFT JOIN"; + return "JOIN"; } }; @@ -567,14 +673,18 @@ namespace sqlite_orm { * O is on(...) argument type. */ template - struct join_t { + struct join_t : join_string { using type = T; using on_type = O; on_type constraint; + join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct left_outer_join_string { operator std::string() const { - return "JOIN"; + return "LEFT OUTER JOIN"; } }; @@ -584,14 +694,18 @@ namespace sqlite_orm { * O is on(...) argument type. */ template - struct left_outer_join_t { + struct left_outer_join_t : left_outer_join_string { using type = T; using on_type = O; on_type constraint; + left_outer_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct on_string { operator std::string() const { - return "LEFT OUTER JOIN"; + return "ON"; } }; @@ -600,14 +714,12 @@ namespace sqlite_orm { * T is on type argument. */ template - struct on_t { - using type = T; + struct on_t : on_string { + using arg_type = T; - type t; + arg_type arg; - operator std::string() const { - return "ON"; - } + on_t(arg_type arg_) : arg(std::move(arg_)) {} }; /** @@ -615,75 +727,87 @@ namespace sqlite_orm { */ template struct using_t { - F O::*column; + F O::*column = nullptr; operator std::string() const { return "USING"; } }; + struct inner_join_string { + operator std::string() const { + return "INNER JOIN"; + } + }; + /** * INNER JOIN holder. * T is joined type which represents any mapped table. * O is on(...) argument type. */ template - struct inner_join_t { + struct inner_join_t : inner_join_string { using type = T; using on_type = O; on_type constraint; + inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct exists_string { operator std::string() const { - return "INNER JOIN"; + return "EXISTS"; } }; template - struct exists_t : condition_t { + struct exists_t : condition_t, exists_string { using type = T; using self = exists_t; type t; - exists_t() = default; - exists_t(T t_) : t(std::move(t_)) {} - operator std::string() const { - return "EXISTS"; - } - negated_condition_t operator!() const { return {*this}; } }; + struct having_string { + operator std::string() const { + return "HAVING"; + } + }; + /** * HAVING holder. * T is having argument type. */ template - struct having_t { + struct having_t : having_string { using type = T; type t; + having_t(type t_) : t(std::move(t_)) {} + }; + + struct cast_string { operator std::string() const { - return "HAVING"; + return "CAST"; } }; template - struct cast_t { + struct cast_t : cast_string { using to_type = T; using expression_type = E; expression_type expression; - operator std::string() const { - return "CAST"; - } + cast_t(expression_type expression_) : expression(std::move(expression_)) {} }; } @@ -1030,6 +1154,11 @@ namespace sqlite_orm { return {std::make_tuple(std::forward(args)...)}; } + template + conditions::dynamic_order_by_t dynamic_order_by(const S &storage) { + return {storage}; + } + template conditions::group_by_t group_by(Args&& ...args) { return {std::make_tuple(std::forward(args)...)}; @@ -1041,8 +1170,13 @@ namespace sqlite_orm { } template - conditions::like_t like(A a, T t) { - return {a, t}; + conditions::like_t like(A a, T t) { + return {std::move(a), std::move(t), {}}; + } + + template + conditions::like_t like(A a, T t, E e) { + return {std::move(a), std::move(t), {std::move(e)}}; } template diff --git a/dev/constraints.h b/dev/constraints.h index f42ebab67..7dfa5b984 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -20,24 +20,14 @@ namespace sqlite_orm { } }; - /** - * PRIMARY KEY constraint class. - * Cs is parameter pack which contains columns (member pointer and/or function pointers). Can be empty when used withen `make_column` function. - */ - template - struct primary_key_t { - std::tuple columns; + struct primary_key_base { enum class order_by { unspecified, ascending, descending, }; - order_by asc_option = order_by::unspecified; - primary_key_t(decltype(columns) c):columns(std::move(c)){} - - using field_type = void; // for column iteration. Better be deleted - using constraints_type = std::tuple<>; + order_by asc_option = order_by::unspecified; operator std::string() const { std::string res = "PRIMARY KEY"; @@ -53,6 +43,22 @@ namespace sqlite_orm { } return res; } + }; + + /** + * PRIMARY KEY constraint class. + * Cs is parameter pack which contains columns (member pointers and/or function pointers). Can be empty when used withen `make_column` function. + */ + template + struct primary_key_t : primary_key_base { + using order_by = primary_key_base::order_by; + + std::tuple columns; + + primary_key_t(decltype(columns) c):columns(std::move(c)){} + + using field_type = void; // for column iteration. Better be deleted + using constraints_type = std::tuple<>; primary_key_t asc() const { auto res = *this; @@ -146,17 +152,32 @@ namespace sqlite_orm { return os; } + struct on_update_delete_base { + const bool update; // true if update and false if delete + + operator std::string() const { + if(this->update){ + return "ON UPDATE"; + }else{ + return "ON DELETE"; + } + } + }; + /** * F - foreign key class */ template - struct on_update_delete_t { + struct on_update_delete_t : on_update_delete_base { using foreign_key_type = F; const foreign_key_type &fk; - const bool update; // true if update and false if delete - on_update_delete_t(decltype(fk) fk_, decltype(update) update_, foreign_key_action action_) : fk(fk_), update(update_), _action(action_) {} + on_update_delete_t(decltype(fk) fk_, decltype(update) update, foreign_key_action action_) : + on_update_delete_base{update}, + fk(fk_), + _action(action_) + {} foreign_key_action _action = foreign_key_action::none; @@ -213,14 +234,6 @@ namespace sqlite_orm { operator bool() const { return this->_action != decltype(this->_action)::none; } - - operator std::string() const { - if(this->update){ - return "ON UPDATE"; - }else{ - return "ON DELETE"; - } - } }; template @@ -263,7 +276,7 @@ namespace sqlite_orm { using constraints_type = std::tuple<>; template - void for_each_column(L) {} + void for_each_column(const L &) {} template constexpr bool has_every() const { @@ -286,19 +299,18 @@ namespace sqlite_orm { template foreign_key_t, std::tuple> references(Rs ...references) { - using ret_type = foreign_key_t, std::tuple>; - return ret_type(std::move(this->columns), std::make_tuple(std::forward(references)...)); + return {std::move(this->columns), std::make_tuple(std::forward(references)...)}; } }; #endif struct collate_t { - internal::collate_argument argument; + internal::collate_argument argument = internal::collate_argument::binary; collate_t(internal::collate_argument argument_): argument(argument_) {} operator std::string() const { - std::string res = "COLLATE " + string_from_collate_argument(this->argument); + std::string res = "COLLATE " + this->string_from_collate_argument(this->argument); return res; } @@ -308,6 +320,7 @@ namespace sqlite_orm { case decltype(argument)::nocase: return "NOCASE"; case decltype(argument)::rtrim: return "RTRIM"; } + throw std::system_error(std::make_error_code(orm_error_code::invalid_collate_argument_enum)); } }; @@ -377,7 +390,7 @@ namespace sqlite_orm { template constraints::default_t default_value(T t) { - return {t}; + return {std::move(t)}; } inline constraints::collate_t collate_nocase() { diff --git a/dev/core_functions.h b/dev/core_functions.h index ce1e3d9f9..3955e8c17 100644 --- a/dev/core_functions.h +++ b/dev/core_functions.h @@ -1,11 +1,14 @@ #pragma once #include // std::string -#include // std::make_tuple +#include // std::make_tuple, std::tuple_size #include // std::forward, std::is_base_of, std::enable_if +#include // std::unique_ptr +#include // std::vector #include "conditions.h" #include "operators.h" +#include "is_base_of_template.h" namespace sqlite_orm { @@ -13,257 +16,120 @@ namespace sqlite_orm { /** * Base class for operator overloading + * R - return type + * S - class with operator std::string + * Args - function arguments types */ - struct core_function_t {}; - - /** - * LENGTH(x) function https://sqlite.org/lang_corefunc.html#length - */ - template - struct length_t : public core_function_t { - T t; + template + struct core_function_t : S, internal::arithmetic_t { + using return_type = R; + using string_type = S; + using args_type = std::tuple; - length_t() = default; + static constexpr const size_t args_size = std::tuple_size::value; - length_t(T t_): t(t_) {} + args_type args; + core_function_t(args_type &&args_) : args(std::move(args_)) {} + }; + + struct length_string { operator std::string() const { return "LENGTH"; } }; - /** - * ABS(x) function https://sqlite.org/lang_corefunc.html#abs - */ - template - struct abs_t : public core_function_t { - T t; - - abs_t() = default; - - abs_t(T t_): t(t_) {} - + struct abs_string { operator std::string() const { return "ABS"; } }; - /** - * LOWER(x) function https://sqlite.org/lang_corefunc.html#lower - */ - template - struct lower_t : public core_function_t { - T t; - - lower_t() = default; - - lower_t(T t_): t(t_) {} - + struct lower_string { operator std::string() const { return "LOWER"; } }; - /** - * UPPER(x) function https://sqlite.org/lang_corefunc.html#upper - */ - template - struct upper_t : public core_function_t { - T t; - - upper_t() = default; - - upper_t(T t_): t(t_) {} - + struct upper_string { operator std::string() const { return "UPPER"; } }; - /** - * CHANGES() function https://sqlite.org/lang_corefunc.html#changes - */ - struct changes_t : public core_function_t { - + struct changes_string { operator std::string() const { return "CHANGES"; } }; - /** - * TRIM(X) function https://sqlite.org/lang_corefunc.html#trim - */ - template - struct trim_single_t : public core_function_t { - X x; - - trim_single_t() = default; - - trim_single_t(X x_): x(x_) {} - + struct trim_string { operator std::string() const { return "TRIM"; } }; - /** - * TRIM(X,Y) function https://sqlite.org/lang_corefunc.html#trim - */ - template - struct trim_double_t : public core_function_t { - X x; - Y y; - - trim_double_t() = default; - - trim_double_t(X x_, Y y_): x(x_), y(y_) {} - + struct ltrim_string { operator std::string() const { - return static_cast(trim_single_t(0)); + return "LTRIM"; } }; - /** - * LTRIM(X) function https://sqlite.org/lang_corefunc.html#ltrim - */ - template - struct ltrim_single_t : public core_function_t { - X x; - - ltrim_single_t() = default; - - ltrim_single_t(X x_): x(x_) {} - + struct rtrim_string { operator std::string() const { - return "LTRIM"; + return "RTRIM"; } }; - /** - * LTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#ltrim - */ - template - struct ltrim_double_t : public core_function_t { - X x; - Y y; - - ltrim_double_t() = default; - - ltrim_double_t(X x_, Y y_): x(x_), y(y_) {} - +#if SQLITE_VERSION_NUMBER >= 3007016 + + struct char_string { operator std::string() const { - return static_cast(ltrim_single_t(0)); + return "CHAR"; } }; - /** - * RTRIM(X) function https://sqlite.org/lang_corefunc.html#rtrim - */ - template - struct rtrim_single_t : public core_function_t { - X x; - - rtrim_single_t() = default; - - rtrim_single_t(X x_): x(x_) {} - + struct random_string { operator std::string() const { - return "RTRIM"; + return "RANDOM"; } }; - /** - * RTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#rtrim - */ - template - struct rtrim_double_t : public core_function_t { - X x; - Y y; - - rtrim_double_t() = default; - - rtrim_double_t(X x_, Y y_): x(x_), y(y_) {} - +#endif + + struct coalesce_string { operator std::string() const { - return static_cast(rtrim_single_t(0)); + return "COALESCE"; } }; - - -#if SQLITE_VERSION_NUMBER >= 3007016 - - /** - * CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char - */ - template - struct char_t_ : public core_function_t { - using args_type = std::tuple; - - args_type args; - - char_t_() = default; - - char_t_(args_type args_): args(args_) {} - + struct date_string { operator std::string() const { - return "CHAR"; + return "DATE"; } }; - struct random_t : core_function_t, internal::arithmetic_t { - + struct datetime_string { operator std::string() const { - return "RANDOM"; + return "DATETIME"; } }; -#endif - template - struct date_t : core_function_t { - using modifiers_type = std::tuple; - - T timestring; - modifiers_type modifiers; - - date_t() = default; - - date_t(T timestring_, modifiers_type modifiers_): timestring(timestring_), modifiers(modifiers_) {} - + struct julianday_string { operator std::string() const { - return "DATE"; + return "JULIANDAY"; } }; - template - struct datetime_t : core_function_t { - using modifiers_type = std::tuple; - - T timestring; - modifiers_type modifiers; - - datetime_t() = default; - - datetime_t(T timestring_, modifiers_type modifiers_): timestring(timestring_), modifiers(modifiers_) {} - + struct zeroblob_string { operator std::string() const { - return "DATETIME"; + return "ZEROBLOB"; } }; - template - struct julianday_t : core_function_t, internal::arithmetic_t { - using modifiers_type = std::tuple; - - T timestring; - modifiers_type modifiers; - - julianday_t() = default; - - julianday_t(T timestring_, modifiers_type modifiers_): timestring(timestring_), modifiers(modifiers_) {} - + struct substr_string { operator std::string() const { - return "JULIANDAY"; + return "SUBSTR"; } }; } @@ -275,134 +141,227 @@ namespace sqlite_orm { template< class F, class R, - typename = typename std::enable_if::value>::type> + typename = typename std::enable_if::value>::type> conditions::lesser_than_t operator<(F f, R r) { - return {f, r}; + return {std::move(f), std::move(r)}; } template< class F, class R, - typename = typename std::enable_if::value>::type> + typename = typename std::enable_if::value>::type> conditions::lesser_or_equal_t operator<=(F f, R r) { - return {f, r}; + return {std::move(f), std::move(r)}; } template< class F, class R, - typename = typename std::enable_if::value>::type> + typename = typename std::enable_if::value>::type> conditions::greater_than_t operator>(F f, R r) { - return {f, r}; + return {std::move(f), std::move(r)}; } template< class F, class R, - typename = typename std::enable_if::value>::type> + typename = typename std::enable_if::value>::type> conditions::greater_or_equal_t operator>=(F f, R r) { - return {f, r}; + return {std::move(f), std::move(r)}; } template< class F, class R, - typename = typename std::enable_if::value>::type> + typename = typename std::enable_if::value>::type> conditions::is_equal_t operator==(F f, R r) { - return {f, r}; + return {std::move(f), std::move(r)}; } template< class F, class R, - typename = typename std::enable_if::value>::type> + typename = typename std::enable_if::value>::type> conditions::is_not_equal_t operator!=(F f, R r) { - return {f, r}; + return {std::move(f), std::move(r)}; } - inline core_functions::random_t random() { - return {}; + /** + * LENGTH(x) function https://sqlite.org/lang_corefunc.html#length + */ + template + core_functions::core_function_t length(T &&t) { + std::tuple args{std::forward(t)}; + return {std::move(args)}; } - template> - Res date(T timestring, Args ...modifiers) { - return Res(timestring, std::make_tuple(std::forward(modifiers)...)); + /** + * ABS(x) function https://sqlite.org/lang_corefunc.html#abs + */ + template + core_functions::core_function_t, core_functions::abs_string, T> abs(T &&t) { + std::tuple args{std::forward(t)}; + return {std::move(args)}; } - template> - Res datetime(T timestring, Args ...modifiers) { - return Res(timestring, std::make_tuple(std::forward(modifiers)...)); + /** + * LOWER(x) function https://sqlite.org/lang_corefunc.html#lower + */ + template + core_functions::core_function_t lower(T &&t) { + std::tuple args{std::forward(t)}; + return {std::move(args)}; } - template> - Res julianday(T timestring, Args ...modifiers) { - return Res(timestring, std::make_tuple(std::forward(modifiers)...)); + /** + * UPPER(x) function https://sqlite.org/lang_corefunc.html#upper + */ + template + core_functions::core_function_t upper(T &&t) { + std::tuple args{std::forward(t)}; + return {std::move(args)}; } -#if SQLITE_VERSION_NUMBER >= 3007016 + /** + * CHANGES() function https://sqlite.org/lang_corefunc.html#changes + */ + inline core_functions::core_function_t changes() { + return {{}}; + } - template - core_functions::char_t_ char_(Args&& ...args) { - using result_type = core_functions::char_t_; - return result_type(std::make_tuple(std::forward(args)...)); + /** + * TRIM(X) function https://sqlite.org/lang_corefunc.html#trim + */ + template + core_functions::core_function_t trim(T &&t) { + std::tuple args{std::forward(t)}; + return {std::move(args)}; } -#endif + /** + * TRIM(X,Y) function https://sqlite.org/lang_corefunc.html#trim + */ + template + core_functions::core_function_t trim(X &&x, Y &&y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {std::move(args)}; + } - template> - Res trim(X x) { - return Res(x); + /** + * LTRIM(X) function https://sqlite.org/lang_corefunc.html#ltrim + */ + template + core_functions::core_function_t ltrim(X &&x) { + std::tuple args{std::forward(x)}; + return {std::move(args)}; } - template> - Res trim(X x, Y y) { - return Res(x, y); + /** + * LTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#ltrim + */ + template + core_functions::core_function_t ltrim(X &&x, Y &&y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {std::move(args)}; } - template> - Res ltrim(X x) { - return Res(x); + /** + * RTRIM(X) function https://sqlite.org/lang_corefunc.html#rtrim + */ + template + core_functions::core_function_t rtrim(X &&x) { + std::tuple args{std::forward(x)}; + return {std::move(args)}; } - template> - Res ltrim(X x, Y y) { - return Res(x, y); + /** + * RTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#rtrim + */ + template + core_functions::core_function_t rtrim(X &&x, Y &&y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {std::move(args)}; } - template> - Res rtrim(X x) { - return Res(x); +#if SQLITE_VERSION_NUMBER >= 3007016 + + /** + * CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char + */ + template + core_functions::core_function_t char_(Args&& ...args) { + return {std::make_tuple(std::forward(args)...)}; } - template> - Res rtrim(X x, Y y) { - return Res(x, y); + /** + * RANDOM() function https://www.sqlite.org/lang_corefunc.html#random + */ + inline core_functions::core_function_t random() { + return {{}}; } - inline core_functions::changes_t changes() { - return {}; +#endif + + /** + * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce + */ + template + core_functions::core_function_t coalesce(Args&& ...args) { + return {std::make_tuple(std::forward(args)...)}; } - template - core_functions::length_t length(T t) { - using result_type = core_functions::length_t; - return result_type(t); + /** + * DATE(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + core_functions::core_function_t date(Args &&...args) { + std::tuple t{std::forward(args)...}; + return {std::move(t)}; } - template - core_functions::abs_t abs(T t) { - using result_type = core_functions::abs_t; - return result_type(t); + /** + * DATETIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + core_functions::core_function_t datetime(Args &&...args) { + std::tuple t{std::forward(args)...}; + return {std::move(t)}; + } + + /** + * JULIANDAY(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + core_functions::core_function_t julianday(Args &&...args) { + std::tuple t{std::forward(args)...}; + return {std::move(t)}; + } + + /** + * ZEROBLOB(N) function https://www.sqlite.org/lang_corefunc.html#zeroblob + */ + template + core_functions::core_function_t, core_functions::zeroblob_string, N> zeroblob(N &&n) { + std::tuple args{std::forward(n)}; + return {std::move(args)}; } - template> - Res lower(T t) { - return Res(t); + /** + * SUBSTR(X,Y) function https://www.sqlite.org/lang_corefunc.html#substr + */ + template + core_functions::core_function_t substr(X &&x, Y &&y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {std::move(args)}; } - template> - Res upper(T t) { - return Res(t); + /** + * SUBSTR(X,Y,Z) function https://www.sqlite.org/lang_corefunc.html#substr + */ + template + core_functions::core_function_t substr(X &&x, Y &&y, Z &&z) { + std::tuple args{std::forward(x), std::forward(y), std::forward(z)}; + return {std::move(args)}; } template< diff --git a/dev/database_connection.h b/dev/database_connection.h index 5362cbb4d..5eccadcc5 100644 --- a/dev/database_connection.h +++ b/dev/database_connection.h @@ -15,7 +15,7 @@ namespace sqlite_orm { database_connection(const std::string &filename) { auto rc = sqlite3_open(filename.c_str(), &this->db); if(rc != SQLITE_OK){ - throw std::system_error(std::error_code(sqlite3_errcode(this->db), get_sqlite_error_category())); + throw std::system_error(std::error_code(sqlite3_errcode(this->db), get_sqlite_error_category()), sqlite3_errmsg(this->db)); } } diff --git a/dev/error_code.h b/dev/error_code.h index 684619901..969a2655b 100644 --- a/dev/error_code.h +++ b/dev/error_code.h @@ -18,6 +18,7 @@ namespace sqlite_orm { cannot_start_a_transaction_within_a_transaction, no_active_transaction, incorrect_journal_mode_string, + invalid_collate_argument_enum, }; } @@ -51,6 +52,8 @@ namespace sqlite_orm { return "Cannot start a transaction within a transaction"; case orm_error_code::no_active_transaction: return "No active transaction"; + case orm_error_code::invalid_collate_argument_enum: + return "Invalid collate_argument enum"; default: return "unknown error"; } diff --git a/dev/index.h b/dev/index.h index cf6a5b0c0..896a942dd 100644 --- a/dev/index.h +++ b/dev/index.h @@ -2,6 +2,7 @@ #include // std::tuple, std::make_tuple #include // std::string +#include // std::forward namespace sqlite_orm { @@ -17,17 +18,17 @@ namespace sqlite_orm { columns_type columns; template - void for_each_column_with_constraints(L) {} + void for_each_column_with_constraints(const L &) {} }; } template internal::index_t make_index(const std::string &name, Cols ...cols) { - return {name, false, std::make_tuple(cols...)}; + return {name, false, std::make_tuple(std::forward(cols)...)}; } template internal::index_t make_unique_index(const std::string &name, Cols ...cols) { - return {name, true, std::make_tuple(cols...)}; + return {name, true, std::make_tuple(std::forward(cols)...)}; } } diff --git a/dev/is_base_of_template.h b/dev/is_base_of_template.h new file mode 100644 index 000000000..7b4dbe4d2 --- /dev/null +++ b/dev/is_base_of_template.h @@ -0,0 +1,39 @@ +#pragma once + +#include // std::true_type, std::false_type, std::declval + +namespace sqlite_orm { + + namespace internal { + + /* + * This is because of bug in MSVC, for more information, please visit + * https://stackoverflow.com/questions/34672441/stdis-base-of-for-template-classes/34672753#34672753 + */ +#if defined(_MSC_VER) + template