├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── appveyor.yml ├── docs ├── get-started.md └── orm-lite.md ├── sample ├── makefile ├── sample.cpp ├── sample.pbxproj └── sample.vcxproj ├── src ├── nullable.h ├── ormlite.h ├── sqlite3.c.zip └── sqlite3.h.zip └── test ├── catch.hpp.zip ├── makefile ├── test.cpp ├── test.pbxproj └── test.vcxproj /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | 244 | # Xcode 245 | # 246 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 247 | 248 | ## Build generated 249 | build/ 250 | DerivedData/ 251 | 252 | ## Various settings 253 | *.pbxuser 254 | !default.pbxuser 255 | *.mode1v3 256 | !default.mode1v3 257 | *.mode2v3 258 | !default.mode2v3 259 | *.perspectivev3 260 | !default.perspectivev3 261 | xcuserdata/ 262 | 263 | ## Other 264 | *.moved-aside 265 | *.xccheckout 266 | *.xcscmblueprint 267 | 268 | # SQLite 269 | src/sqlite3.c 270 | src/sqlite3.h 271 | 272 | # Catch 273 | catch.hpp 274 | 275 | # Make Output 276 | *.db 277 | *.o 278 | sample/sample 279 | test/test 280 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: required 3 | compiler: 4 | - gcc 5 | - clang 6 | script: 7 | - unzip -qu src/sqlite3.h.zip -d src 8 | - unzip -qu src/sqlite3.c.zip -d src 9 | - unzip -qu test/catch.hpp.zip -d test 10 | - make -C sample && ./sample/sample 11 | - make -C test && ./test/test 12 | install: 13 | - if [ "$CC" = "gcc" ]; then export CXX="g++-5" CC="gcc-5"; fi 14 | - if [ "$CC" = "clang" ]; then export CXX="clang++-3.8" CC="clang-3.8"; fi 15 | addons: 16 | apt: 17 | sources: 18 | - ubuntu-toolchain-r-test 19 | - llvm-toolchain-precise-3.8 20 | packages: 21 | - gcc-5 22 | - g++-5 23 | - clang-3.8 24 | - unzip -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 BOT Man JL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ORM Lite 2 | 3 | **ORM Lite** is a C++ [_**Object Relation Mapping** (ORM)_](https://en.wikipedia.org/wiki/Object-relational_mapping) 4 | for **SQLite3** (currently 😂), 5 | written in Modern C++ style. 6 | 7 | [![Build status - MSVC](https://ci.appveyor.com/api/projects/status/github/BOT-Man-JL/ORM-Lite?svg=true&branch=master)](https://ci.appveyor.com/project/BOT-Man-JL/ORM-Lite) 8 | [![Build status - gcc/clang](https://travis-ci.org/BOT-Man-JL/ORM-Lite.svg?branch=master)](https://travis-ci.org/BOT-Man-JL/ORM-Lite) 9 | 10 | ## Features 11 | 12 | - **Easy** to Use 13 | - **Header Only** 14 | ([src/ormlite.h](src/ormlite.h), [src/nullable.h](src/nullable.h)) 15 | - **Powerful** Compile-time **Type/DSL Deduction** 16 | 17 | ## Documentation 18 | 19 | #### [Get Started Here](docs/get-started.md) 😉 20 | 21 | #### [Full Document](docs/orm-lite.md) 😊 22 | 23 | ## Planned Features 24 | 25 | - Support More Databases (Looking for a Better Driver recently...) 26 | - Customized Primary Key (Hard to Design an Elegant Interface for it...) 27 | - Blob (Hard to be Serialized to Script...) 28 | - Date/Time Types (Weak Typed in SQL...) 29 | - Subquery (Too Complicated... the Interface would be) 30 | 31 | Feel free to [Issue](https://github.com/BOT-Man-JL/ORM-Lite/issues/new), 32 | if you have any idea. 😎 33 | 34 | ## Implementation Details (实现细节) 35 | 36 | Posts in **Chinese** only: 37 | 38 | - [How to Design a Naive C++ ORM](https://BOT-Man-JL.github.io/articles/?post=2016/How-to-Design-a-Naive-Cpp-ORM) 39 | - [How to Design a Better C++ ORM](https://bot-man-jl.github.io/articles/?post=2016/How-to-Design-a-Better-Cpp-ORM) 40 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | image: Visual Studio 2017 3 | build_script: 4 | - ps: Expand-Archive src/sqlite3.h.zip -DestinationPath src 5 | - ps: Expand-Archive src/sqlite3.c.zip -DestinationPath src 6 | - ps: Expand-Archive test/catch.hpp.zip -DestinationPath test 7 | - ps: msbuild /verbosity:minimal sample/sample.vcxproj "/p:Configuration=Debug;Platform=Win32" 8 | - ps: msbuild /verbosity:minimal sample/sample.vcxproj "/p:Configuration=Debug;Platform=x64" 9 | - ps: msbuild /verbosity:minimal sample/sample.vcxproj "/p:Configuration=Release;Platform=Win32" 10 | - ps: msbuild /verbosity:minimal sample/sample.vcxproj "/p:Configuration=Release;Platform=x64" 11 | - ps: msbuild /verbosity:minimal test/test.vcxproj "/p:Configuration=Debug;Platform=Win32" 12 | - ps: msbuild /verbosity:minimal test/test.vcxproj "/p:Configuration=Debug;Platform=x64" 13 | - ps: msbuild /verbosity:minimal test/test.vcxproj "/p:Configuration=Release;Platform=Win32" 14 | - ps: msbuild /verbosity:minimal test/test.vcxproj "/p:Configuration=Release;Platform=x64" 15 | test_script: 16 | - ps: sample\Debug\sample\sample.exe 17 | - ps: sample\Release\sample.exe 18 | - ps: sample\x64\Debug\sample\sample.exe 19 | - ps: sample\x64\Release\sample.exe 20 | - ps: test\Debug\test\test.exe 21 | - ps: test\Release\test.exe 22 | - ps: test\x64\Debug\test\test.exe 23 | - ps: test\x64\Release\test.exe 24 | -------------------------------------------------------------------------------- /docs/get-started.md: -------------------------------------------------------------------------------- 1 | # Get Started 2 | 3 | Here is a **short tour** for this **Amazing** ORM 😉 4 | 5 | The full code is in [sample.cpp](../sample/sample.cpp). 6 | 7 | ## Preparation 8 | 9 | Before we start, Include **src** into your Project: 10 | 11 | - `ORMLite.h` 12 | - **SQLite3** Dependency 13 | 14 | > If you haven't installed **SQLite3**, 15 | > you can find the zip file of `sqlite3.h` and `sqlite3.c` in `/src` 16 | 17 | ## Including *ORM Lite* 18 | 19 | ``` cpp 20 | #include "ORMLite.h" 21 | using namespace BOT_ORM; 22 | using namespace BOT_ORM::Expression; 23 | 24 | struct UserModel 25 | { 26 | int user_id; 27 | std::string user_name; 28 | double credit_count; 29 | 30 | Nullable age; 31 | Nullable salary; 32 | Nullable title; 33 | 34 | // Inject ORM-Lite into this Class :-) 35 | ORMAP ("UserModel", user_id, user_name, credit_count, 36 | age, salary, title); 37 | }; 38 | ``` 39 | 40 | `Nullable` helps us construct `Nullable` Value in C++, 41 | which is described in the [Document](orm-lite.md) 😁 42 | 43 | In this sample, `ORMAP ("UserModel", ...)` do that: 44 | - `Class UserModel` will be mapped into `TABLE UserModel`; 45 | - NOT `Nullable` members will be mapped as `NOT NULL`; 46 | - `int, double, std::string` will be mapped into 47 | `INT, REAL, TEXT` respectively; 48 | - The first entry `id` will be set as the **Primary Key** of the Table; 49 | 50 | And then we define and inject other 2 Classes in this sample: 51 | 52 | ``` cpp 53 | struct SellerModel 54 | { 55 | int seller_id; 56 | std::string seller_name; 57 | double credit_count; 58 | 59 | // Inject ORM-Lite into this Class :-) 60 | ORMAP ("SellerModel", seller_id, seller_name, credit_count); 61 | }; 62 | 63 | struct OrderModel 64 | { 65 | int order_id; 66 | int user_id; 67 | int seller_id; 68 | std::string product_name; 69 | Nullable fee; 70 | 71 | // Inject ORM-Lite into this Class :-) 72 | ORMAP ("OrderModel", order_id, user_id, seller_id, 73 | product_name, fee); 74 | }; 75 | ``` 76 | 77 | ## Field Extracting 78 | 79 | - `FieldExtractor` 80 | 81 | ``` cpp 82 | // Define more Query Helper Objects and their Field Extractor 83 | UserModel user; 84 | SellerModel seller; 85 | OrderModel order; 86 | auto field = FieldExtractor { user, seller, order }; 87 | 88 | // Extract Field from 'field' 89 | // For example: field (user.user_name) 90 | // => Retrieve the field of user_name in UserModel table 91 | ``` 92 | 93 | ## Working on *Database* with *ORMapper* 94 | 95 | - `ORMapper` 96 | 97 | ``` cpp 98 | // Open a Connection with 'sample.db' 99 | ORMapper mapper ("sample.db"); 100 | ``` 101 | 102 | ## Create or Drop Tables 103 | 104 | - `ORMapper.CreateTbl` 105 | - `ORMapper.DropTbl` 106 | - `Expression` 107 | 108 | ``` cpp 109 | // Create Table with Constraints 110 | mapper.CreateTbl ( 111 | UserModel {}, 112 | Constraint::Default (field (user.salary), 1000.0)); 113 | 114 | // Remarks: 115 | // CREATE TABLE UserModel( 116 | // user_id INTEGER NOT NULL PRIMARY KEY, 117 | // user_name TEXT NOT NULL, 118 | // credit_count REAL NOT NULL, 119 | // age INTEGER, 120 | // salary REAL DEFAULT 1000, 121 | // title TEXT); 122 | 123 | mapper.CreateTbl ( 124 | SellerModel {}, 125 | Constraint::Check (field (seller.credit_count) > 0.0)); 126 | 127 | // Remarks: 128 | // CREATE TABLE SellerModel( 129 | // seller_id INTEGER NOT NULL PRIMARY KEY, 130 | // seller_name TEXT NOT NULL, 131 | // credit_count REAL NOT NULL, 132 | // CHECK (credit_count > 0)); 133 | 134 | mapper.CreateTbl ( 135 | OrderModel {}, 136 | Constraint::Reference ( 137 | field (order.user_id), field (user.user_id)), 138 | Constraint::Reference ( 139 | field (order.seller_id), field (seller.seller_id)), 140 | Constraint::Unique (Constraint::CompositeField { 141 | field (order.product_name), field (order.fee) })); 142 | 143 | // Remarks: 144 | // CREATE TABLE OrderModel( 145 | // order_id INTEGER NOT NULL PRIMARY KEY, 146 | // user_id INTEGER NOT NULL, 147 | // seller_id INTEGER NOT NULL, 148 | // product_name TEXT NOT NULL, 149 | // fee REAL, 150 | // FOREIGN KEY (user_id) REFERENCES UserModel(user_id), 151 | // FOREIGN KEY (seller_id) REFERENCES SellerModel(seller_id), 152 | // UNIQUE (product_name, fee)); 153 | 154 | ... 155 | 156 | // Drop Tables 157 | mapper.DropTbl (UserModel {}); 158 | mapper.DropTbl (SellerModel {}); 159 | mapper.DropTbl (OrderModel {}); 160 | ``` 161 | 162 | #### User Model 163 | 164 | | user_id| user_name| credit_count| age| salary| title| 165 | |--------|----------|-------------|-------|--------|-------| 166 | | 0| John| 0.2| 21| 1000.0| `null`| 167 | | 1| Jack| 0.4| `null`| 3.14| `null`| 168 | | 2| Jess| 0.6| `null`| 1000.0| Dr.| 169 | | ...| ...| ...| ...| ...| ...| 170 | 171 | #### Seller Model 172 | 173 | | seller_id| seller_name| credit_count| 174 | |----------|------------|-------------| 175 | | ...| ...| ...| 176 | 177 | #### Order Model 178 | 179 | | order_id| user_id| seller_id| product_name| fee| 180 | |---------|--------|----------|-------------|----| 181 | | ...| ...| ...| ...| ...| 182 | 183 | ## Basic CRUD 184 | 185 | - `ORMapper.Insert` (Create) 186 | - `ORMapper.Query` (Read) 187 | - `ORMapper.Update` (Update) 188 | - `ORMapper.Delete` (Delete) 189 | - `ORMapper.Transaction` 190 | - `Table Constraints` 191 | 192 | ``` cpp 193 | std::vector initObjs = 194 | { 195 | { 0, "John", 0.2, 21, nullptr, nullptr }, 196 | { 1, "Jack", 0.4, nullptr, 3.14, nullptr }, 197 | { 2, "Jess", 0.6, nullptr, nullptr, std::string ("Dr.") } 198 | }; 199 | 200 | // Insert Values with Primary Key 201 | for (const auto &obj : initObjs) 202 | mapper.Insert (obj); 203 | 204 | initObjs[1].salary = nullptr; 205 | initObjs[1].title = "St."; 206 | 207 | // Update Entity by Primary Key (WHERE UserModel.id = 1) 208 | mapper.Update (initObjs[1]); 209 | 210 | // Delete Entity by Primary Key (WHERE UserModel.id = 2) 211 | mapper.Delete (initObjs[2]); 212 | 213 | // Transactional Statements 214 | try 215 | { 216 | mapper.Transaction ([&] () 217 | { 218 | mapper.Delete (initObjs[0]); // OK 219 | mapper.Insert (UserModel { 220 | 1, "Joke", 0, nullptr, nullptr, nullptr 221 | }); // Failed 222 | }); 223 | } 224 | catch (const std::exception &ex) 225 | { 226 | // If any statement Failed, throw an exception 227 | 228 | std::cout << ex.what () << std::endl; 229 | // SQL error: 'UNIQUE constraint failed: UserModel.id' 230 | 231 | // Remarks: 232 | // mapper.Delete (initObjs[0]); will not applied :-) 233 | } 234 | 235 | // Select All to List 236 | auto result1 = mapper.Query (UserModel {}).ToList (); 237 | 238 | // decltype (result1) == std::list 239 | // result1 = [{ 0, 0.2, "John", 21, 1000, null }, 240 | // { 1, 0.4, "Jack", null, null, "St." }] 241 | 242 | // Table Constraints 243 | try 244 | { 245 | // Insert Values without Primary Key 246 | mapper.Insert (SellerModel { 0, "John Inc.", 0.0 }, false); 247 | } 248 | catch (const std::exception &ex) 249 | { 250 | std::cout << ex.what () << std::endl; 251 | // SQL error: 'CHECK constraint failed: SellerModel' 252 | } 253 | ``` 254 | 255 | ## Batch Operations 256 | 257 | - `ORMapper.InsertRange` 258 | - `ORMapper.UpdateRange` 259 | 260 | ``` cpp 261 | std::vector dataToSeed; 262 | for (int i = 50; i < 100; i++) 263 | dataToSeed.emplace_back (UserModel { 264 | i, "July_" + std::to_string (i), i * 0.2, 265 | nullptr, nullptr, nullptr 266 | }); 267 | 268 | // Insert by Batch Insert 269 | mapper.Transaction ([&] () { 270 | mapper.InsertRange (dataToSeed); 271 | }); 272 | 273 | for (size_t i = 0; i < 20; i++) 274 | { 275 | dataToSeed[i + 30].age = 30 + (int) i / 2; 276 | dataToSeed[i + 20].title = "Mr. " + std::to_string (i); 277 | } 278 | 279 | // Update by Batch Update 280 | mapper.Transaction ([&] () { 281 | // Note that: it will erase the default value of 'salary' 282 | mapper.UpdateRange (dataToSeed); 283 | }); 284 | ``` 285 | 286 | ## Single-Table Query 287 | 288 | - `Expression` 289 | - `Aggregate` 290 | - `Queryable.Where` 291 | - `Queryable.OrderBy` 292 | - `Queryable.Take` 293 | - `Queryable.Skip` 294 | - `Queryable.Select` 295 | 296 | ``` cpp 297 | // Select by Condition 298 | auto result2 = mapper.Query (UserModel {}) 299 | .Where ( 300 | field (user.user_name) & std::string ("July%") && 301 | (field (user.age) >= 32 && 302 | field (user.title) != nullptr) 303 | ) 304 | .OrderByDescending (field (user.age)) 305 | .OrderBy (field (user.user_id)) 306 | .Take (3) 307 | .Skip (1) 308 | .ToVector (); 309 | 310 | // Remarks: 311 | // SELECT * FROM UserModel 312 | // WHERE (user_name LIKE 'July%' AND 313 | // (age >= 32 AND title IS NOT NULL)) 314 | // ORDER BY age DESC, user_id 315 | // LIMIT 3 OFFSET 1 316 | 317 | // decltype (result2) == std::vector 318 | // result2 = [{ 89, 17.8, "July_89", 34, null, "Mr. 19" }, 319 | // { 86, 17.2, "July_86", 33, null, "Mr. 16" }, 320 | // { 87, 17.4, "July_87", 33, null, "Mr. 17" }] 321 | 322 | // Calculate Aggregate Function 323 | auto avg = mapper.Query (UserModel {}) 324 | .Where (field (user.user_name) & std::string ("July%")) 325 | .Aggregate (Avg (field (user.credit_count))); 326 | 327 | // Remarks: 328 | // SELECT AVG (credit_count) FROM UserModel 329 | // WHERE (user_name LIKE 'July%') 330 | 331 | // avg = 14.9 332 | 333 | auto count = mapper.Query (UserModel {}) 334 | .Where (field (user.user_name) | std::string ("July%")) 335 | .Aggregate (Count ()); 336 | 337 | // Remarks: 338 | // SELECT COUNT (*) FROM UserModel 339 | // WHERE (user_name NOT LIKE 'July%') 340 | 341 | // count = 2 342 | ``` 343 | 344 | ## Update / Delete by Statement 345 | 346 | - `Expression` 347 | - `ORMapper.Update` 348 | - `ORMapper.Delete` 349 | 350 | ``` cpp 351 | // Update by Condition 352 | mapper.Update ( 353 | UserModel {}, 354 | (field (user.age) = 10) && 355 | (field (user.credit_count) = 1.0), 356 | field (user.user_name) == std::string ("July")); 357 | 358 | // Remarks: 359 | // UPDATE UserModel SET age = 10,credit_count = 1.0 360 | // WHERE (user_name = 'July') 361 | 362 | // Delete by Condition 363 | mapper.Delete (UserModel {}, 364 | field (user.user_id) >= 90); 365 | 366 | // Remarks: 367 | // DELETE FROM UserModel WHERE (id >= 90) 368 | ``` 369 | 370 | ## Multi-Table Query 371 | 372 | - `Expression` 373 | - `Aggregate` 374 | - `Queryable.Join` 375 | - `Queryable.LeftJoin` 376 | - `Queryable.Select` 377 | - `Queryable.GroupBy` 378 | - `Queryable.Having` 379 | - `Queryable.Union` 380 | 381 | ``` cpp 382 | mapper.Transaction ([&] () 383 | { 384 | for (size_t i = 0; i < 50; i++) 385 | { 386 | mapper.Insert ( 387 | SellerModel { (int) i + 50, 388 | "Seller " + std::to_string (i), 3.14 }); 389 | mapper.Insert ( 390 | OrderModel { 0, 391 | (int) i / 2 + 50, 392 | (int) i / 4 + 50, 393 | "Item " + std::to_string (i), 394 | i * 0.5 }, false); 395 | } 396 | }); 397 | 398 | // Join Tables for Query 399 | auto joinedQuery = mapper.Query (UserModel {}) 400 | .Join (OrderModel {}, 401 | field (user.user_id) == 402 | field (order.user_id)) 403 | .LeftJoin (SellerModel {}, 404 | field (seller.seller_id) == 405 | field (order.seller_id)) 406 | .Where (field (user.user_id) >= 65); 407 | 408 | // Get Result to List 409 | auto result3 = joinedQuery.ToList (); 410 | 411 | // Remarks: 412 | // SELECT * FROM UserModel 413 | // JOIN OrderModel 414 | // ON UserModel.user_id=OrderModel.user_id 415 | // LEFT JOIN SellerModel 416 | // ON SellerModel.seller_id=OrderModel.seller_id 417 | // WHERE (UserModel.user_id>=65) 418 | 419 | // decltype (result3) == std::list, ..>> 420 | // result3 = [(65, "July_65", 13, null, null, null, 421 | // 31, 65, 57, "Item 30", 15, 422 | // 57, "Seller 7", 3.14), 423 | // (65, "July_65", 13, null, null, null, 424 | // 32, 65, 57, "Item 31", 15.5, 425 | // 57, "Seller 7", 3.14), 426 | // ... ] 427 | 428 | // Group & Having ~ 429 | auto result4 = joinedQuery 430 | .Select (field (order.user_id), 431 | field (user.user_name), 432 | Avg (field (order.fee))) 433 | .GroupBy (field (user.user_name)) 434 | .Having (Sum (field (order.fee)) >= 40.5) 435 | .Skip (3) 436 | .ToList (); 437 | 438 | // Remarks: 439 | // SELECT OrderModel.user_id, 440 | // UserModel.user_name, 441 | // AVG (OrderModel.fee) 442 | // FROM UserModel 443 | // JOIN OrderModel 444 | // ON UserModel.user_id=OrderModel.user_id 445 | // LEFT JOIN SellerModel 446 | // ON SellerModel.seller_id=OrderModel.seller_id 447 | // WHERE (UserModel.user_id>=65) 448 | // GROUP BY UserModel.user_name 449 | // HAVING SUM (OrderModel.fee)>=40.5 450 | // LIMIT ~0 OFFSET 3 451 | 452 | // decltype (result4) == std::list, ..>> 453 | // result4 = [(73, "July_73", 23.25), 454 | // (74, "July_74", 24.25)] 455 | 456 | // Compound Select 457 | // Results are Nullable-Tuples 458 | auto result5 = mapper.Query (OrderModel {}) 459 | .Select (field (order.product_name), field (order.user_id)) 460 | .Where (field (order.user_id) == 50) 461 | .Union ( 462 | joinedQuery 463 | .Select (field (user.user_name), field (order.order_id)) 464 | ) 465 | .Take (4) 466 | .ToList (); 467 | 468 | // Remarks: 469 | // SELECT OrderModel.product_name, 470 | // OrderModel.user_id 471 | // FROM OrderModel 472 | // WHERE (OrderModel.user_id==50) 473 | // UNION 474 | // SELECT UserModel.user_name, 475 | // OrderModel.order_id 476 | // FROM UserModel 477 | // JOIN OrderModel 478 | // ON UserModel.user_id=OrderModel.user_id 479 | // LEFT JOIN SellerModel 480 | // ON SellerModel.seller_id=OrderModel.seller_id 481 | // WHERE (UserModel.user_id>=65) 482 | // LIMIT 4; 483 | 484 | // decltype (result5) == std::list, ..>> 485 | // result5 = [("Item 0", 50), 486 | // ("Item 1", 50), 487 | // ("July_65", 31), 488 | // ("July_65", 32)] 489 | ``` 490 | 491 | ## Wrapping Up 492 | 493 | This little ORM is very cool, enjoy it. 😇 494 | 495 | See the **[Full Document Here](orm-lite.md)**. 496 | -------------------------------------------------------------------------------- /docs/orm-lite.md: -------------------------------------------------------------------------------- 1 | # ORM Lite 2 | 3 | ## Requirements 4 | 5 | - **C++ 14** Support 6 | - MSVC >= 14 (VS 2015 Update 3) 7 | - gcc >= 5.4 8 | - Clang >= 3.8 9 | - **SQLite 3** Dependency 10 | 11 | ## Mapping 12 | 13 | | SQL Concepts | C++ Concepts | Notes | 14 | |---------------------|--------------|-------| 15 | | Table / Relation | Object | Deduced by `ORMAP` | 16 | | Tuple (in `SELECT`) | `std::tuple` | Deduced by `Join` / `Select` | 17 | | Column | Field | Decuced by `Field` | 18 | 19 | ## `BOT_ORM` 20 | 21 | ``` cpp 22 | #include "ORMLite.h" 23 | using namespace BOT_ORM; 24 | using namespace BOT_ORM::Expression; 25 | ``` 26 | 27 | Macro `ORMAP` in `ORMLite.h` 28 | 29 | - `ORMAP (TableName, PrimaryKey, ...);` 30 | 31 | Modules under `namespace BOT_ORM` 32 | 33 | - `BOT_ORM::Nullable` 34 | - `BOT_ORM::ORMapper` 35 | - `BOT_ORM::Queryable` 36 | - `BOT_ORM::FieldExtractor` 37 | - `BOT_ORM::Constraint` 38 | 39 | Modules under `namespace BOT_ORM::Expression` 40 | 41 | - `BOT_ORM::Expression::Selectable` 42 | - `BOT_ORM::Expression::Field` 43 | - `BOT_ORM::Expression::NullableField` 44 | - `BOT_ORM::Expression::Aggregate` 45 | - `BOT_ORM::Expression::Expr` 46 | - `BOT_ORM::Expression::SetExpr` 47 | - `BOT_ORM::Expression::Count ()` 48 | - `BOT_ORM::Expression::Sum ()` 49 | - `BOT_ORM::Expression::Avg ()` 50 | - `BOT_ORM::Expression::Max ()` 51 | - `BOT_ORM::Expression::Min ()` 52 | 53 | Static Modules under `class BOT_ORM::Constraint` 54 | 55 | - `BOT_ORM::Constraint::CompositeField` 56 | - `BOT_ORM::Constraint::Default` 57 | - `BOT_ORM::Constraint::Check` 58 | - `BOT_ORM::Constraint::Unique` 59 | - `BOT_ORM::Constraint::Reference` 60 | 61 | ## `ORMAP (TableName, PrimaryKey, ...)` 62 | 63 | Before we use ORM Lite, we should **Inject** some code into the Class; 64 | 65 | ``` cpp 66 | struct MyClass 67 | { 68 | int field1; 69 | double field2; 70 | std::string field3; 71 | 72 | Nullable field4; 73 | Nullable field5; 74 | Nullable field6; 75 | 76 | // Inject ORM-Lite into this Class :-) 77 | ORMAP ("TableName", field1, field2, field3, 78 | field4, field5, field6); 79 | }; 80 | ``` 81 | 82 | In this sample, `ORMAP ("TableName", field1, ...)` specifies that: 83 | - Class `MyClass` will be mapped into Table `TableName`; 84 | - `field1, field2, field3, field4, field5, field6` will be mapped 85 | into `INTEGER field1 NOT NULL`, `REAL field2 NOT NULL`, 86 | `TEXT field3 NOT NULL`, `INTEGER field4`, `REAL field5` 87 | and `TEXT field6` respectively; 88 | - The first entry `field1` will be set as the **Primary Key** 89 | of the Table; 90 | 91 | Note that: 92 | - You should Pass **at least 2** Params into this Macro 93 | (TableName and PrimaryKey), otherwise it will Not Compile...; 94 | - Currently Only Support 95 | - T such that `std::is_integral::value == true` 96 | and **NOT** `char` or `*char_t` 97 | - T such that `std::is_floating_point::value == true` 98 | - T such that `std::is_same::value == true` 99 | - which are mapped as `INTEGER`, `REAL` and `TEXT` (SQLite3); 100 | - Not `Nullable` members will be mapped as `NOT NULL`; 101 | - The **Primary Key** (first entry) is **Recommended** to be 102 | `Integral`, and it could be regarded as the `ROWID` 103 | with a Better Query **Performance** :-) 104 | (otherwise SQLite 3 will Generate a Column for `ROWID` Implicitly) 105 | - Field Names MUST **NOT** be SQL Keywords (SQL Constraint); 106 | - `std::string` Value MUST **NOT** contain `\0` (SQL Constraint); 107 | - `std::string` Value can be **utf-8** to support **Locale**; 108 | - `ORMAP (...)` will **auto** Inject some **private members**; 109 | - `__Accept ()` to Implement **Visitor Pattern**; 110 | - `__Tuple ()` to **Flatten** data to tuple; 111 | - `__FieldNames ()` and `__TableName` to store strings; 112 | - and the Access by Implementation; 113 | 114 | ## `BOT_ORM::Nullable` 115 | 116 | It keeps the *Similar Semantic* of `Nullable` as `C#`; and 117 | [Reference Here](https://stackoverflow.com/questions/2537942/nullable-values-in-c/28811646#28811646) 118 | 119 | ### Construction & Assignment 120 | 121 | - Default Constructed / `nullptr` Constructed / `nullptr` Assigned 122 | object is `NULL` Valued; 123 | - Value Constructed / Value Assigned object is `NOT NULL` Valued 124 | - `Nullable` Objects are **Copyable** / **Movable**, 125 | and the *Destination* Value has the *Same Value* as the *Source* 126 | 127 | ``` cpp 128 | // Default or Null Construction 129 | Nullable (); 130 | Nullable (nullptr_t); 131 | 132 | // Null Assignment 133 | const Nullable & operator= (nullptr_t); 134 | 135 | // Value Construction 136 | Nullable (const T &value); 137 | 138 | // Value Assignment 139 | const Nullable & operator= (const T &value); 140 | ``` 141 | 142 | ### Get Value 143 | 144 | Return the `Underlying Value` of the object or 145 | `Default Not-null Value` of `T`; 146 | (similar to `GetValueOrDefault` in C#) 147 | 148 | ``` cpp 149 | const T &Value (); const 150 | ``` 151 | 152 | ### Comparison 153 | 154 | Two Objects have the Same value only if their `Nullable` Construction: 155 | - Both are `NULL`; 156 | - Both are `NOT NULL` and have the Same `Underlying Value`; 157 | 158 | ``` cpp 159 | bool operator==(const Nullable &op1, const Nullable &op2); 160 | bool operator==(const Nullable &op1, T &op2); 161 | bool operator==(T &op1, const Nullable &op2); 162 | bool operator==(const Nullable &op1, nullptr_t); 163 | bool operator==(nullptr_t, const Nullable &op2); 164 | ``` 165 | 166 | ## `BOT_ORM::ORMapper` 167 | 168 | ### Connection 169 | 170 | ``` cpp 171 | ORMapper (const string &connectionString); 172 | ``` 173 | 174 | Remarks: 175 | - Construct a **O/R Mapper** to connect to `connectionString`; 176 | - For SQLite, the `connectionString` is the **database name**; 177 | - The `ORMapper` **Keeps** the **Connection** 178 | and **Shares** the **Connection** with `Queryable`; 179 | - **Disconnecting** at all related `ORMapper`/`Queryable` destructied; 180 | 181 | ### Transaction 182 | 183 | ``` cpp 184 | void Transaction (Fn fn); 185 | ``` 186 | 187 | Remarks: 188 | - Invoke `fn` **Transactionally**, as following: 189 | 190 | ``` cpp 191 | try 192 | { 193 | _connector.Execute ("begin transaction;"); 194 | fn (); 195 | _connector.Execute ("commit transaction;"); 196 | } 197 | catch (...) 198 | { 199 | _connector.Execute ("rollback transaction;"); 200 | throw; 201 | } 202 | ``` 203 | 204 | ### Create and Drop Table 205 | 206 | ``` cpp 207 | // Create Table 208 | void CreateTbl (const MyClass &); 209 | void CreateTbl (const MyClass &, 210 | const Constraint &constraint1, 211 | const Constraint &constraint2, 212 | ...); 213 | 214 | // Drop Table 215 | void DropTbl (const MyClass &); 216 | ``` 217 | 218 | Remarks: 219 | - Create/Drop Table for class `MyClass`; 220 | - `void CreateTbl (const MyClass &, ...);` 221 | will Create a Table with **Constraints**; 222 | - `Constraint` will be described later; 223 | 224 | SQL: 225 | 226 | ``` sql 227 | CREATE TABLE MyClass (...); 228 | 229 | DROP TABLE MyClass; 230 | ``` 231 | 232 | ### Insert 233 | 234 | ``` cpp 235 | // Insert a single value 236 | void Insert (const MyClass &entity, bool withId = true); 237 | 238 | // Insert values 239 | void InsertRange (const Container &entities, bool withId = true); 240 | ``` 241 | 242 | Remarks: 243 | - Insert `entity` / `entities` into Table for `MyClass`; 244 | - If `withId` is `false`, it will insert the `entity` 245 | without **Primary Key**; 246 | - Note that: **Primary Key** is recommended to be **Integral** 247 | in this case (**INT PK** would be `AUTOINCREMENT`, 248 | Floating Point / String may **Failed**); 249 | - **NULL** Fields will **NOT** be Set; 250 | - `entities` must **SUPPORT** `forward_iterator`; 251 | 252 | SQL: 253 | 254 | ``` sql 255 | INSERT INTO MyClass (...) VALUES (...); 256 | 257 | INSERT INTO MyClass (...) VALUES (...); 258 | INSERT INTO MyClass (...) VALUES (...); 259 | ... 260 | ``` 261 | 262 | ### Update 263 | 264 | ``` cpp 265 | // Update value by Primary Key 266 | void Update (const MyClass &entity); 267 | 268 | // Update values by Primary Key 269 | void UpdateRange (const Container &entities); 270 | 271 | // Update by Expressions 272 | void Update (const MyClass &, 273 | const Expression::SetExpr &setExpr, 274 | const Expression::Expr &whereExpr); 275 | ``` 276 | 277 | Remarks: 278 | - Update `entity` / `entities` in Table `MyClass` 279 | with the Same **Primary Key**; 280 | - Update Set `setExpr` Where `whereExpr` for Table `MyClass` 281 | (`Expressions` will be described later); 282 | - **NULL** Fields will also be Set; 283 | - `entities` must **SUPPORT** `forward_iterator`; 284 | 285 | SQL: 286 | 287 | ``` sql 288 | UPDATE MyClass SET (...) WHERE KEY = ; 289 | 290 | UPDATE MyClass SET (...) WHERE KEY = ; 291 | UPDATE MyClass SET (...) WHERE KEY = ; 292 | ... 293 | 294 | UPDATE MyClass SET (...) WHERE ...; 295 | ``` 296 | 297 | ### Delete 298 | 299 | ``` cpp 300 | // Delete value by Primary Key 301 | void Delete (const MyClass &entity); 302 | 303 | // Delete by Expressions 304 | void Delete (const MyClass &, 305 | const Expression::Expr &whereExpr); 306 | ``` 307 | 308 | Remarks: 309 | - Delete Entry in Table `MyClass` with the Same **Primary Key**; 310 | - Delete Where `whereExpr` for Table `MyClass` 311 | (`Expression` will be described later); 312 | - This function will **NOT** throw a `std::runtime_error` 313 | even if there is nothing to delete; 314 | 315 | SQL: 316 | 317 | ``` sql 318 | DELETE FROM MyClass WHERE KEY = ; 319 | 320 | DELETE FROM MyClass WHERE ...; 321 | ``` 322 | 323 | ### Query 324 | 325 | ``` cpp 326 | // Retrieve a Queryable Object 327 | Queryable Query (MyClass queryHelper); 328 | ``` 329 | 330 | Remarks: 331 | - Return new `Queryable` object with `QueryResult` is `MyClass`; 332 | - `MyClass` **MUST** be **Copy Constructible** 333 | to Construct a `queryHelper`; 334 | - The `ORMapper` **Shares** the **Connection** with `Queryable`; 335 | 336 | ## `BOT_ORM::Queryable` 337 | 338 | ### Retrieve Results 339 | 340 | ``` cpp 341 | Nullable Aggregate (const Expression::Aggregate &agg) const; 342 | std::vector ToVector () const; 343 | std::list ToList () const; 344 | ``` 345 | 346 | Remarks: 347 | - `QueryResult` specifies the **Row Type** of Query Result; 348 | - `Aggregate` will Get the one-or-zero-row Result for `agg`; 349 | - `ToVector` / `ToList` returns the Collection of `QueryResult`; 350 | - The results are from the **Connection** of `Queryable`; 351 | - If the Result is `null` for `NOT Nullable` Field, 352 | it will throw `std::runtime_error`; 353 | - `Expression` will be described later; 354 | 355 | ### Set Conditions 356 | 357 | ``` cpp 358 | Queryable Distinct (bool isDistinct = true) const; 359 | Queryable Where (const Expression::Expr &expr) const; 360 | 361 | Queryable GroupBy (const Expression::Field &field) const; 362 | Queryable Having (const Expression::Expr &expr) const; 363 | 364 | Queryable OrderBy (const Expression::Field &field) const; 365 | Queryable OrderByDescending (const Expression::Field &field) const; 366 | 367 | Queryable Take (size_t count) const; 368 | Queryable Skip (size_t count) const; 369 | ``` 370 | 371 | Remarks: 372 | - Default Selection is `ALL`; 373 | - These functions will Set/Append Conditions to a copy of `this`; 374 | - `OrderBy` will **Append** `field` to Condition, 375 | while Other functions will **Set** `DISTINCT`, 376 | `expr`, `field` or `count` to Condition; 377 | - New `Queryable` **Shares** the **Connection** of `this`; 378 | - `Expression` will be described later; 379 | 380 | ### Construct New `QueryResult` 381 | 382 | ``` cpp 383 | auto Select (const Expression::Selectable &target1, 384 | const Expression::Selectable &target2, 385 | ...) const; 386 | auto Join (const MyClass2 &queryHelper2, 387 | const Expression::Expr &onExpr) const; 388 | auto LeftJoin (const MyClass2 &queryHelper2, 389 | const Expression::Expr &onExpr) const; 390 | ``` 391 | 392 | Remarks: 393 | - Default Selection is **All Columns** (`SELECT *`); 394 | - `Select` will Set `QueryResult` to `std::tuple`, 395 | which can be retrieved by `SELECT target1, target2, ...` 396 | (`target` can be **Field** or **Aggregate Functions**); 397 | - `Join` / `LeftJoin` will Set `QueryResult` to `std::tuple<...>` 398 | - `...` is the **flattened nullable concatenation** of all entries of 399 | **Previous** `QueryResult` and `queryHelper2`; 400 | - **Flatten** and **Nullable** means all fields of `...` are 401 | all `Nullable`, where `T` is the Supported Types of `ORMAP` 402 | (NOT `std::tuple` or `MyClass`); 403 | - `onExpr` specifies the `ON` Expression for `JOIN`; 404 | - All Functions will copy the **Conditions** of `this` to the new one; 405 | - New `Queryable` **Shares** the **Connection** of `this`; 406 | - `Expression` will be described later; 407 | 408 | ### Compound Select 409 | 410 | ``` cpp 411 | Queryable Union (const Queryable &queryable) const; 412 | Queryable UnionAll (const Queryable &queryable) const; 413 | Queryable Intersect (const Queryable &queryable) const; 414 | Queryable Expect (const Queryable &queryable) const; 415 | ``` 416 | 417 | Remarks: 418 | - All Functions will return a Compound Select 419 | for `this` and `queryable`; 420 | - All Functions will only **Inherit Conditions** 421 | `OrderBy` and `Limit` from `this`; 422 | - New `Queryable` **Shares** the **Connection** of `this`; 423 | 424 | ### Query SQL 425 | 426 | We will use the following SQL to Query: 427 | 428 | ``` sql 429 | SELECT [DISTINCT] ... 430 | FROM TABLE 431 | [LEFT] JOIN TABLE ON ... 432 | ... 433 | WHERE ... 434 | GROUP BY 435 | HAVING ... 436 | [ SELECT ... FROM ...] 437 | ORDER BY [DESC], ... 438 | LIMIT OFFSET ; 439 | ``` 440 | 441 | Remarks: 442 | - If the Corresponding Condition is NOT Set, it will be omitted; 443 | 444 | ## `BOT_ORM::FieldExtractor` 445 | 446 | ``` cpp 447 | // Construction 448 | FieldExtractor (const MyClass1 &queryHelper1, 449 | const MyClass2 &queryHelper2, 450 | ...); 451 | 452 | // Get Field<> by operator () 453 | Field operator () (const T &field) const; 454 | NullableField operator () (const Nullable &field) const; 455 | ``` 456 | 457 | Remarks: 458 | - Construction of `FieldExtractor` will take all fields' pointers of 459 | `queryHelper` into a **Hash Table**; 460 | - `operator () (field)` will find the position of `field` 461 | in the **Hash Table** from `queryHelper` 462 | and Construct the corresponding `Field`; 463 | - If the `field` is `Nullable` 464 | it will Construct a `NullableField`; 465 | and it will Construct a `Field` otherwise; 466 | - If `field` is not a member of `queryHelper`, 467 | it will throw `std::runtime_error`; 468 | - `Expression` will be described later; 469 | 470 | ## `namespace BOT_ORM::Expression` 471 | 472 | ### Fields and Aggregate Functions 473 | 474 | #### Definitions 475 | 476 | ``` cpp 477 | BOT_ORM::Expression::Selectable 478 | BOT_ORM::Expression::Field : public Selectable 479 | BOT_ORM::Expression::NullableField : public Field 480 | BOT_ORM::Expression::Aggregate : public Selectable 481 | ``` 482 | 483 | #### Operations 484 | 485 | ``` cpp 486 | // Field / Aggregate ? Value 487 | Expr operator == (const Selectable &op, T value); 488 | Expr operator != (const Selectable &op, T value); 489 | Expr operator > (const Selectable &op, T value); 490 | Expr operator >= (const Selectable &op, T value); 491 | Expr operator < (const Selectable &op, T value); 492 | Expr operator <= (const Selectable &op, T value); 493 | 494 | // Field ? Field 495 | Expr operator == (const Field &op1, const Field &op2); 496 | Expr operator != (const Field &op1, const Field &op2); 497 | Expr operator > (const Field &op1, const Field &op2); 498 | Expr operator >= (const Field &op1, const Field &op2); 499 | Expr operator < (const Field &op1, const Field &op2); 500 | Expr operator <= (const Field &op1, const Field &op2); 501 | 502 | // Nullable Field ? nullptr 503 | Expr operator == (const NullableField &op, nullptr_t); 504 | Expr operator !== (const NullableField &op, nullptr_t); 505 | 506 | // String Field ? std::string 507 | Expr operator& (const Field &op, std::string val); 508 | Expr operator| (const Field &op, std::string val); 509 | 510 | // Get SetExpr 511 | SetExpr operator = (const Field &op, T value); 512 | SetExpr operator = (const NullableField &op, nullptr_t); 513 | ``` 514 | 515 | Remarks: 516 | - `Selectable ? T` returns `Expr`; 517 | - `Field ? Field` returns `Expr`; 518 | - `NullableField == / != nullptr` 519 | returns `Expr IS NULL / IS NOT NULL`; 520 | - `Field & / | T` 521 | returns `Expr LIKE / NOT LIKE `; 522 | - `Field = T` returns `SetExpr = `; 523 | - `NullableField = nullptr` returns `SetExpr = null`; 524 | 525 | ### Expressions 526 | 527 | #### Definitions 528 | 529 | ``` cpp 530 | BOT_ORM::Expression::Expr 531 | BOT_ORM::Expression::SetExpr 532 | ``` 533 | 534 | #### Operations 535 | 536 | ``` cpp 537 | // Get Composite Expr 538 | Expr operator && (const Expr &op1, const Expr &op2); 539 | Expr operator || (const Expr &op1, const Expr &op2); 540 | 541 | // Concatenate 2 SetExpr 542 | SetExpr operator && (const SetExpr &op1, const SetExpr &op2); 543 | ``` 544 | 545 | Remarks: 546 | - `Expr && / || Expr` returns `( and / or )`; 547 | - `SetExpr && SetExpr` returns `, `; 548 | 549 | ### Aggregate Function Helpers 550 | 551 | ``` cpp 552 | BOT_ORM::Expression::Count (); 553 | BOT_ORM::Expression::Count (const Field &field); 554 | BOT_ORM::Expression::Sum (const Field &field); 555 | BOT_ORM::Expression::Avg (const Field &field); 556 | BOT_ORM::Expression::Max (const Field &field); 557 | BOT_ORM::Expression::Min (const Field &field); 558 | ``` 559 | 560 | Remarks: 561 | 562 | They will Generate Aggregate Functions as: 563 | 564 | - `size_t COUNT (*)` 565 | - `size_t COUNT (field)` 566 | - `T SUM (field)` 567 | - `T AVG (field)` 568 | - `T MAX (field)` 569 | - `T MIN (field)` 570 | 571 | ## `class BOT_ORM::Constraint` 572 | 573 | ### Composite Field 574 | 575 | `CompositeField` could be constructed from normal `Field` 576 | and is used by **Constraint Functions**; 577 | 578 | ``` cpp 579 | CompositeField (const Expression::Field &field1, 580 | const Expression::Field &field2, 581 | ...); 582 | ``` 583 | 584 | Remarks: 585 | - Composite Field will Contain fields in the given **Order**; 586 | - If the `field`s are not from the Same Table, 587 | it will throw `std::runtime_error`; 588 | 589 | ### Generate Constraints 590 | 591 | ``` cpp 592 | Constraint Default (const Expression::Field &field, 593 | const T &value); 594 | Constraint Check (const Expression::Expr &expr); 595 | Constraint Unique (const Expression::Field &field); 596 | Constraint Unique (const CompositeField &fields); 597 | Constraint Reference(const Expression::Field &field, 598 | const Expression::Field &refered); 599 | Constraint Reference (const CompositeField &field, 600 | const CompositeField &refered); 601 | ``` 602 | 603 | Remarks: 604 | - They will Generate Constraints as: 605 | `DEFAULT`, `CHECK`, `UNIQUE` and `FOREIGN KEY`; 606 | - `FOREIGN KEY` is Enabled by Default 607 | (during the Construction of `ORMapper`); 608 | - Why there is NO **Composite Primary Key** Constaints: 609 | - It's recommended to use a **Integral** Field as the Primary Key, 610 | described in Section ## Macro `ORMAP`; 611 | - We can use `Unique` to implement a **Composite Unique** Constraint; 612 | - And use `Reference` to define a **Composite Foreign Key**; 613 | 614 | ## Error Handling 615 | 616 | ### Compile-time Error 617 | 618 | ORM Lite uses `static_assert` to Check if the Code is valid: 619 | 620 | - **Forget** to **Place** `ORMAP` into the Class 621 | > Please Inject the Class with 'ORMAP' first 622 | - Place **Unsupported Types** into `ORMAP` 623 | > Only Support Integral, Floating Point and std::string 624 | 625 | Note that: Error Messages will often appear at the **TOP**; 626 | 627 | ### Runtime Error 628 | 629 | All Functions will throw `std::runtime_error` 630 | with the **Error Message** if Failed: 631 | 632 | - Failed to **Connect** to **Database** 633 | > SQL error: Can't open database `` 634 | - Failed at Executing **Query** Script 635 | > SQL error: `` at `` 636 | - Query Result's **Column Count** does **NOT Match** the Expected Count 637 | (happening in **NOT** *Code First* Cases...) 638 | > SQL error: Bad Column Count at `` 639 | - Get `NULL` from Query while the Expected **Field** is **NOT NULL** 640 | (happening in **NOT** *Code First* Cases...) 641 | > SQL error: Get Null Value at `` 642 | - Pass a **Non-Member** Var of Registered Object to Field **Extractor** 643 | > No Such Field for current Extractor 644 | - **Composite** Fields from **NOT** the Same Tables 645 | > Fields are NOT from the Same Table -------------------------------------------------------------------------------- /sample/makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET = sample 3 | SOURCES = sample.cpp ../src/sqlite3.c 4 | WARNINGFLAGS = -Wall -W 5 | CPPFLAGS = -std=c++14 6 | LINKS = -lstdc++ -lpthread -ldl 7 | 8 | OBJS = $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES))) 9 | 10 | %.o: %.c 11 | $(CC) -c $< -o $@ $(WARNINGFLAGS) $(LINKS) 12 | 13 | %.o: %.cpp 14 | $(CC) -c $< -o $@ $(WARNINGFLAGS) $(LINKS) $(CPPFLAGS) 15 | 16 | $(TARGET): $(OBJS) 17 | $(CC) $(OBJS) -o $(TARGET) $(LINKS) 18 | 19 | clean: 20 | rm -rf $(OBJS) $(TARGET) *.db 21 | -------------------------------------------------------------------------------- /sample/sample.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Sample of ORM Lite 3 | // https://github.com/BOT-Man-JL/ORM-Lite 4 | // BOT Man, 2016 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../src/ormlite.h" 11 | using namespace BOT_ORM; 12 | using namespace BOT_ORM::Expression; 13 | 14 | namespace PrintHelper 15 | { 16 | template 17 | void PrintNullable (const BOT_ORM::Nullable &val) 18 | { 19 | if (val == nullptr) 20 | std::cout << "null"; 21 | else 22 | std::cout << val.Value (); 23 | } 24 | 25 | template 26 | struct TuplePrinter 27 | { 28 | static void print (const Tuple& t) 29 | { 30 | TuplePrinter::print (t); 31 | std::cout << ", "; 32 | PrintNullable (std::get (t)); 33 | } 34 | }; 35 | 36 | template 37 | struct TuplePrinter 38 | { 39 | static void print (const Tuple& t) 40 | { 41 | PrintNullable (std::get<0> (t)); 42 | } 43 | }; 44 | 45 | template 46 | void PrintTuple (const std::tuple& t) 47 | { 48 | std::cout << "("; 49 | TuplePrinter::print (t); 50 | std::cout << ")\n"; 51 | } 52 | 53 | template 54 | void PrintTuples (const Container &vals) 55 | { 56 | for (const auto &val : vals) 57 | PrintTuple (val); 58 | std::cout << std::endl; 59 | } 60 | } 61 | 62 | struct UserModel 63 | { 64 | int user_id; 65 | std::string user_name; 66 | double credit_count; 67 | 68 | Nullable age; 69 | Nullable salary; 70 | Nullable title; 71 | 72 | // Inject ORM-Lite into this Class :-) 73 | ORMAP ("UserModel", user_id, user_name, credit_count, age, salary, title); 74 | }; 75 | 76 | struct SellerModel 77 | { 78 | int seller_id; 79 | std::string seller_name; 80 | double credit_count; 81 | 82 | // Inject ORM-Lite into this Class :-) 83 | ORMAP ("SellerModel", seller_id, seller_name, credit_count); 84 | }; 85 | 86 | struct OrderModel 87 | { 88 | int order_id; 89 | int user_id; 90 | int seller_id; 91 | std::string product_name; 92 | Nullable fee; 93 | 94 | // Inject ORM-Lite into this Class :-) 95 | ORMAP ("OrderModel", order_id, user_id, seller_id, product_name, fee); 96 | }; 97 | 98 | int main () 99 | { 100 | /* 101 | ## Field Extracting 102 | */ 103 | 104 | // Define more Query Helper Objects and their Field Extractor 105 | UserModel user; 106 | SellerModel seller; 107 | OrderModel order; 108 | auto field = FieldExtractor { user, seller, order }; 109 | 110 | // Extract Field from 'field' 111 | // For example: field (user.user_name) 112 | // => Retrieve the field of user_name in UserModel table 113 | 114 | /* 115 | ## Working on *Database* with *ORMapper* 116 | */ 117 | 118 | // Open a Connection with 'sample.db' 119 | ORMapper mapper ("sample.db"); 120 | 121 | /* 122 | ## Create or Drop Tables 123 | */ 124 | 125 | try 126 | { 127 | // Drop Tables 128 | mapper.DropTbl (OrderModel {}); 129 | mapper.DropTbl (UserModel {}); 130 | mapper.DropTbl (SellerModel {}); 131 | } 132 | catch (...) {} 133 | 134 | // Create Table with Constraints 135 | mapper.CreateTbl ( 136 | UserModel {}, 137 | Constraint::Default (field (user.salary), 1000.0)); 138 | 139 | // Remarks: 140 | // CREATE TABLE UserModel( 141 | // user_id INTEGER NOT NULL PRIMARY KEY, 142 | // user_name TEXT NOT NULL, 143 | // credit_count REAL NOT NULL, 144 | // age INTEGER, 145 | // salary REAL DEFAULT 1000, 146 | // title TEXT); 147 | 148 | mapper.CreateTbl ( 149 | SellerModel {}, 150 | Constraint::Check (field (seller.credit_count) > 0.0)); 151 | 152 | // Remarks: 153 | // CREATE TABLE SellerModel( 154 | // seller_id INTEGER NOT NULL PRIMARY KEY, 155 | // seller_name TEXT NOT NULL, 156 | // credit_count REAL NOT NULL, 157 | // CHECK (credit_count > 0)); 158 | 159 | mapper.CreateTbl ( 160 | OrderModel {}, 161 | Constraint::Reference ( 162 | field (order.user_id), field (user.user_id)), 163 | Constraint::Reference ( 164 | field (order.seller_id), field (seller.seller_id)), 165 | Constraint::Unique (Constraint::CompositeField { 166 | field (order.product_name), field (order.fee) })); 167 | 168 | // Remarks: 169 | // CREATE TABLE OrderModel( 170 | // order_id INTEGER NOT NULL PRIMARY KEY, 171 | // user_id INTEGER NOT NULL, 172 | // seller_id INTEGER NOT NULL, 173 | // product_name TEXT NOT NULL, 174 | // fee REAL, 175 | // FOREIGN KEY (user_id) REFERENCES UserModel(user_id), 176 | // FOREIGN KEY (seller_id) REFERENCES SellerModel(seller_id), 177 | // UNIQUE (product_name, fee)); 178 | 179 | /* 180 | ## Basic CRUD 181 | */ 182 | 183 | std::vector initObjs = 184 | { 185 | { 0, "John", 0.2, 21, nullptr, nullptr }, 186 | { 1, "Jack", 0.4, nullptr, 3.14, nullptr }, 187 | { 2, "Jess", 0.6, nullptr, nullptr, std::string ("Dr.") } 188 | }; 189 | 190 | // Insert Values with Primary Key 191 | for (const auto &obj : initObjs) 192 | mapper.Insert (obj); 193 | 194 | initObjs[1].salary = nullptr; 195 | initObjs[1].title = "St."; 196 | 197 | // Update Entity by Primary Key (WHERE UserModel.id = 1) 198 | mapper.Update (initObjs[1]); 199 | 200 | // Delete Entity by Primary Key (WHERE UserModel.id = 2) 201 | mapper.Delete (initObjs[2]); 202 | 203 | // Transactional Statements 204 | try 205 | { 206 | mapper.Transaction ([&] () 207 | { 208 | mapper.Delete (initObjs[0]); // OK 209 | mapper.Insert (UserModel { 210 | 1, "Joke", 0, nullptr, nullptr, nullptr 211 | }); // Failed 212 | }); 213 | } 214 | catch (const std::exception &ex) 215 | { 216 | // If any statement Failed, throw an exception 217 | 218 | std::cout << ex.what () << std::endl; 219 | // SQL error: 'UNIQUE constraint failed: UserModel.id' 220 | 221 | // Remarks: 222 | // mapper.Delete (initObjs[0]); will not applied :-) 223 | } 224 | 225 | // Select All to List 226 | auto result1 = mapper.Query (UserModel {}).ToList (); 227 | 228 | // decltype (result1) == std::list 229 | // result1 = [{ 0, 0.2, "John", 21, 1000, null }, 230 | // { 1, 0.4, "Jack", null, null, "St." }] 231 | 232 | // Table Constraints 233 | try 234 | { 235 | // Insert Values without Primary Key 236 | mapper.Insert (SellerModel { 0, "John Inc.", 0.0 }, false); 237 | } 238 | catch (const std::exception &ex) 239 | { 240 | std::cout << ex.what () << std::endl; 241 | // SQL error: 'CHECK constraint failed: SellerModel' 242 | } 243 | 244 | /* 245 | ## Batch Operations 246 | */ 247 | 248 | std::vector dataToSeed; 249 | for (int i = 50; i < 100; i++) 250 | dataToSeed.emplace_back (UserModel { 251 | i, "July_" + std::to_string (i), i * 0.2, 252 | nullptr, nullptr, nullptr 253 | }); 254 | 255 | // Insert by Batch Insert 256 | mapper.Transaction ([&] () { 257 | mapper.InsertRange (dataToSeed); 258 | }); 259 | 260 | for (size_t i = 0; i < 20; i++) 261 | { 262 | dataToSeed[i + 30].age = 30 + (int) i / 2; 263 | dataToSeed[i + 20].title = "Mr. " + std::to_string (i); 264 | } 265 | 266 | // Update by Batch Update 267 | mapper.Transaction ([&] () { 268 | // Note that: it will erase the default value of 'salary' 269 | mapper.UpdateRange (dataToSeed); 270 | }); 271 | 272 | /* 273 | ## Single-Table Query 274 | */ 275 | 276 | // Select by Condition 277 | auto result2 = mapper.Query (UserModel {}) 278 | .Where ( 279 | field (user.user_name) & std::string ("July%") && 280 | (field (user.age) >= 32 && 281 | field (user.title) != nullptr) 282 | ) 283 | .OrderByDescending (field (user.age)) 284 | .OrderBy (field (user.user_id)) 285 | .Take (3) 286 | .Skip (1) 287 | .ToVector (); 288 | 289 | // Remarks: 290 | // SELECT * FROM UserModel 291 | // WHERE (user_name LIKE 'July%' AND 292 | // (age >= 32 AND title IS NOT NULL)) 293 | // ORDER BY age DESC, user_id 294 | // LIMIT 3 OFFSET 1 295 | 296 | // decltype (result2) == std::vector 297 | // result2 = [{ 89, 17.8, "July_89", 34, null, "Mr. 19" }, 298 | // { 86, 17.2, "July_86", 33, null, "Mr. 16" }, 299 | // { 87, 17.4, "July_87", 33, null, "Mr. 17" }] 300 | 301 | // Calculate Aggregate Function 302 | auto avg = mapper.Query (UserModel {}) 303 | .Where (field (user.user_name) & std::string ("July%")) 304 | .Aggregate (Avg (field (user.credit_count))); 305 | 306 | // Remarks: 307 | // SELECT AVG (credit_count) FROM UserModel 308 | // WHERE (user_name LIKE 'July%') 309 | 310 | // avg = 14.9 311 | 312 | auto count = mapper.Query (UserModel {}) 313 | .Where (field (user.user_name) | std::string ("July%")) 314 | .Aggregate (Count ()); 315 | 316 | // Remarks: 317 | // SELECT COUNT (*) FROM UserModel 318 | // WHERE (user_name NOT LIKE 'July%') 319 | 320 | // count = 2 321 | 322 | /* 323 | ## Update / Delete by Statement 324 | */ 325 | 326 | // Update by Condition 327 | mapper.Update ( 328 | UserModel {}, 329 | (field (user.age) = 10) && 330 | (field (user.credit_count) = 1.0), 331 | field (user.user_name) == std::string ("July")); 332 | 333 | // Remarks: 334 | // UPDATE UserModel SET age = 10,credit_count = 1.0 335 | // WHERE (user_name = 'July') 336 | 337 | // Delete by Condition 338 | mapper.Delete (UserModel {}, 339 | field (user.user_id) >= 90); 340 | 341 | // Remarks: 342 | // DELETE FROM UserModel WHERE (id >= 90) 343 | 344 | /* 345 | ## Multi-Table Query 346 | */ 347 | 348 | mapper.Transaction ([&] () 349 | { 350 | for (size_t i = 0; i < 50; i++) 351 | { 352 | mapper.Insert ( 353 | SellerModel { (int) i + 50, 354 | "Seller " + std::to_string (i), 3.14 }); 355 | mapper.Insert ( 356 | OrderModel { 0, 357 | (int) i / 2 + 50, 358 | (int) i / 4 + 50, 359 | "Item " + std::to_string (i), 360 | i * 0.5 }, false); 361 | } 362 | }); 363 | 364 | // Join Tables for Query 365 | auto joinedQuery = mapper.Query (UserModel {}) 366 | .Join (OrderModel {}, 367 | field (user.user_id) == 368 | field (order.user_id)) 369 | .LeftJoin (SellerModel {}, 370 | field (seller.seller_id) == 371 | field (order.seller_id)) 372 | .Where (field (user.user_id) >= 65); 373 | 374 | // Get Result to List 375 | auto result3 = joinedQuery.ToList (); 376 | 377 | // Remarks: 378 | // SELECT * FROM UserModel 379 | // JOIN OrderModel 380 | // ON UserModel.user_id=OrderModel.user_id 381 | // LEFT JOIN SellerModel 382 | // ON SellerModel.seller_id=OrderModel.seller_id 383 | // WHERE (UserModel.user_id>=65) 384 | 385 | // decltype (result3) == std::list, ..>> 386 | // result3 = [(65, "July_65", 13, null, null, null, 387 | // 31, 65, 57, "Item 30", 15, 388 | // 57, "Seller 7", 3.14), 389 | // (65, "July_65", 13, null, null, null, 390 | // 32, 65, 57, "Item 31", 15.5, 391 | // 57, "Seller 7", 3.14), 392 | // ... ] 393 | 394 | // Group & Having ~ 395 | auto result4 = joinedQuery 396 | .Select (field (order.user_id), 397 | field (user.user_name), 398 | Avg (field (order.fee))) 399 | .GroupBy (field (user.user_name)) 400 | .Having (Sum (field (order.fee)) >= 40.5) 401 | .Skip (3) 402 | .ToList (); 403 | 404 | // Remarks: 405 | // SELECT OrderModel.user_id, 406 | // UserModel.user_name, 407 | // AVG (OrderModel.fee) 408 | // FROM UserModel 409 | // JOIN OrderModel 410 | // ON UserModel.user_id=OrderModel.user_id 411 | // LEFT JOIN SellerModel 412 | // ON SellerModel.seller_id=OrderModel.seller_id 413 | // WHERE (UserModel.user_id>=65) 414 | // GROUP BY UserModel.user_name 415 | // HAVING SUM (OrderModel.fee)>=40.5 416 | // LIMIT ~0 OFFSET 3 417 | 418 | // decltype (result4) == std::list, ..>> 419 | // result4 = [(73, "July_73", 23.25), 420 | // (74, "July_74", 24.25)] 421 | 422 | // Compound Select 423 | // Results are Nullable-Tuples 424 | auto result5 = mapper.Query (OrderModel {}) 425 | .Select (field (order.product_name), field (order.user_id)) 426 | .Where (field (order.user_id) == 50) 427 | .Union ( 428 | joinedQuery 429 | .Select (field (user.user_name), field (order.order_id)) 430 | ) 431 | .Take (4) 432 | .ToList (); 433 | 434 | // Remarks: 435 | // SELECT OrderModel.product_name, 436 | // OrderModel.user_id 437 | // FROM OrderModel 438 | // WHERE (OrderModel.user_id==50) 439 | // UNION 440 | // SELECT UserModel.user_name, 441 | // OrderModel.order_id 442 | // FROM UserModel 443 | // JOIN OrderModel 444 | // ON UserModel.user_id=OrderModel.user_id 445 | // LEFT JOIN SellerModel 446 | // ON SellerModel.seller_id=OrderModel.seller_id 447 | // WHERE (UserModel.user_id>=65) 448 | // LIMIT 4; 449 | 450 | // decltype (result5) == std::list, ..>> 451 | // result5 = [("Item 0", 50), 452 | // ("Item 1", 50), 453 | // ("July_65", 31), 454 | // ("July_65", 32)] 455 | 456 | // ==================================================== 457 | 458 | // Output UserModel Objects 459 | auto printUserModeles = [] (const auto &objs) 460 | { 461 | for (const auto& item : objs) 462 | { 463 | std::cout << item.user_id << "\t" << item.credit_count 464 | << "\t" << item.user_name << "\t"; 465 | PrintHelper::PrintNullable (item.age); 466 | std::cout << "\t"; 467 | PrintHelper::PrintNullable (item.salary); 468 | std::cout << "\t"; 469 | PrintHelper::PrintNullable (item.title); 470 | std::cout << "\n"; 471 | } 472 | std::cout << std::endl; 473 | }; 474 | 475 | // Sec 1 476 | std::cout << "\n"; 477 | printUserModeles (result1); 478 | 479 | // Sec 2 480 | printUserModeles (result2); 481 | PrintHelper::PrintNullable (count); 482 | std::cout << "\n"; 483 | PrintHelper::PrintNullable (avg); 484 | std::cout << "\n" << std::endl; 485 | 486 | // Sec 3 487 | PrintHelper::PrintTuples (result3); 488 | PrintHelper::PrintTuples (result4); 489 | PrintHelper::PrintTuples (result5); 490 | 491 | //std::cin.get (); 492 | return 0; 493 | } 494 | -------------------------------------------------------------------------------- /sample/sample.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1781A76A1F5FF9EE008E6067 /* sample.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1781A7691F5FF9EE008E6067 /* sample.cpp */; }; 11 | 1781A76F1F5FF9FC008E6067 /* sqlite3.c in Sources */ = {isa = PBXBuildFile; fileRef = 1781A76D1F5FF9FC008E6067 /* sqlite3.c */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | 1781A75D1F5FF992008E6067 /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = /usr/share/man/man1/; 19 | dstSubfolderSpec = 0; 20 | files = ( 21 | ); 22 | runOnlyForDeploymentPostprocessing = 1; 23 | }; 24 | /* End PBXCopyFilesBuildPhase section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 1781A75F1F5FF992008E6067 /* sample */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = sample; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 1781A7691F5FF9EE008E6067 /* sample.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sample.cpp; path = sample.cpp; sourceTree = ""; }; 29 | 1781A76B1F5FF9FC008E6067 /* nullable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nullable.h; path = ../src/nullable.h; sourceTree = ""; }; 30 | 1781A76C1F5FF9FC008E6067 /* ormlite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ormlite.h; path = ../src/ormlite.h; sourceTree = ""; }; 31 | 1781A76D1F5FF9FC008E6067 /* sqlite3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sqlite3.c; path = ../src/sqlite3.c; sourceTree = ""; }; 32 | 1781A76E1F5FF9FC008E6067 /* sqlite3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sqlite3.h; path = ../src/sqlite3.h; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | 1781A75C1F5FF992008E6067 /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | 1781A7561F5FF992008E6067 = { 47 | isa = PBXGroup; 48 | children = ( 49 | 1781A76B1F5FF9FC008E6067 /* nullable.h */, 50 | 1781A76C1F5FF9FC008E6067 /* ormlite.h */, 51 | 1781A76D1F5FF9FC008E6067 /* sqlite3.c */, 52 | 1781A76E1F5FF9FC008E6067 /* sqlite3.h */, 53 | 1781A7691F5FF9EE008E6067 /* sample.cpp */, 54 | 1781A7601F5FF992008E6067 /* Products */, 55 | ); 56 | sourceTree = ""; 57 | }; 58 | 1781A7601F5FF992008E6067 /* Products */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 1781A75F1F5FF992008E6067 /* sample */, 62 | ); 63 | name = Products; 64 | sourceTree = ""; 65 | }; 66 | /* End PBXGroup section */ 67 | 68 | /* Begin PBXNativeTarget section */ 69 | 1781A75E1F5FF992008E6067 /* sample */ = { 70 | isa = PBXNativeTarget; 71 | buildConfigurationList = 1781A7661F5FF992008E6067 /* Build configuration list for PBXNativeTarget "sample" */; 72 | buildPhases = ( 73 | 1781A75B1F5FF992008E6067 /* Sources */, 74 | 1781A75C1F5FF992008E6067 /* Frameworks */, 75 | 1781A75D1F5FF992008E6067 /* CopyFiles */, 76 | ); 77 | buildRules = ( 78 | ); 79 | dependencies = ( 80 | ); 81 | name = sample; 82 | productName = sample; 83 | productReference = 1781A75F1F5FF992008E6067 /* sample */; 84 | productType = "com.apple.product-type.tool"; 85 | }; 86 | /* End PBXNativeTarget section */ 87 | 88 | /* Begin PBXProject section */ 89 | 1781A7571F5FF992008E6067 /* Project object */ = { 90 | isa = PBXProject; 91 | attributes = { 92 | LastUpgradeCheck = 0830; 93 | TargetAttributes = { 94 | 1781A75E1F5FF992008E6067 = { 95 | CreatedOnToolsVersion = 8.3.3; 96 | ProvisioningStyle = Automatic; 97 | }; 98 | }; 99 | }; 100 | buildConfigurationList = 1781A75A1F5FF992008E6067 /* Build configuration list for PBXProject "sample" */; 101 | compatibilityVersion = "Xcode 3.2"; 102 | developmentRegion = English; 103 | hasScannedForEncodings = 0; 104 | knownRegions = ( 105 | en, 106 | ); 107 | mainGroup = 1781A7561F5FF992008E6067; 108 | productRefGroup = 1781A7601F5FF992008E6067 /* Products */; 109 | projectDirPath = ""; 110 | projectRoot = ""; 111 | targets = ( 112 | 1781A75E1F5FF992008E6067 /* sample */, 113 | ); 114 | }; 115 | /* End PBXProject section */ 116 | 117 | /* Begin PBXSourcesBuildPhase section */ 118 | 1781A75B1F5FF992008E6067 /* Sources */ = { 119 | isa = PBXSourcesBuildPhase; 120 | buildActionMask = 2147483647; 121 | files = ( 122 | 1781A76A1F5FF9EE008E6067 /* sample.cpp in Sources */, 123 | 1781A76F1F5FF9FC008E6067 /* sqlite3.c in Sources */, 124 | ); 125 | runOnlyForDeploymentPostprocessing = 0; 126 | }; 127 | /* End PBXSourcesBuildPhase section */ 128 | 129 | /* Begin XCBuildConfiguration section */ 130 | 1781A7641F5FF992008E6067 /* Debug */ = { 131 | isa = XCBuildConfiguration; 132 | buildSettings = { 133 | ALWAYS_SEARCH_USER_PATHS = NO; 134 | CLANG_ANALYZER_NONNULL = YES; 135 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 136 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 137 | CLANG_CXX_LIBRARY = "libc++"; 138 | CLANG_ENABLE_MODULES = YES; 139 | CLANG_ENABLE_OBJC_ARC = YES; 140 | CLANG_WARN_BOOL_CONVERSION = YES; 141 | CLANG_WARN_CONSTANT_CONVERSION = YES; 142 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 143 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 144 | CLANG_WARN_EMPTY_BODY = YES; 145 | CLANG_WARN_ENUM_CONVERSION = YES; 146 | CLANG_WARN_INFINITE_RECURSION = YES; 147 | CLANG_WARN_INT_CONVERSION = YES; 148 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 149 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 150 | CLANG_WARN_UNREACHABLE_CODE = YES; 151 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 152 | CODE_SIGN_IDENTITY = "-"; 153 | COPY_PHASE_STRIP = NO; 154 | DEBUG_INFORMATION_FORMAT = dwarf; 155 | ENABLE_STRICT_OBJC_MSGSEND = YES; 156 | ENABLE_TESTABILITY = YES; 157 | GCC_C_LANGUAGE_STANDARD = gnu99; 158 | GCC_DYNAMIC_NO_PIC = NO; 159 | GCC_NO_COMMON_BLOCKS = YES; 160 | GCC_OPTIMIZATION_LEVEL = 0; 161 | GCC_PREPROCESSOR_DEFINITIONS = ( 162 | "DEBUG=1", 163 | "$(inherited)", 164 | ); 165 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 166 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 167 | GCC_WARN_UNDECLARED_SELECTOR = YES; 168 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 169 | GCC_WARN_UNUSED_FUNCTION = YES; 170 | GCC_WARN_UNUSED_VARIABLE = YES; 171 | MACOSX_DEPLOYMENT_TARGET = 10.12; 172 | MTL_ENABLE_DEBUG_INFO = YES; 173 | ONLY_ACTIVE_ARCH = YES; 174 | SDKROOT = macosx; 175 | }; 176 | name = Debug; 177 | }; 178 | 1781A7651F5FF992008E6067 /* Release */ = { 179 | isa = XCBuildConfiguration; 180 | buildSettings = { 181 | ALWAYS_SEARCH_USER_PATHS = NO; 182 | CLANG_ANALYZER_NONNULL = YES; 183 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 184 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 185 | CLANG_CXX_LIBRARY = "libc++"; 186 | CLANG_ENABLE_MODULES = YES; 187 | CLANG_ENABLE_OBJC_ARC = YES; 188 | CLANG_WARN_BOOL_CONVERSION = YES; 189 | CLANG_WARN_CONSTANT_CONVERSION = YES; 190 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 191 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 192 | CLANG_WARN_EMPTY_BODY = YES; 193 | CLANG_WARN_ENUM_CONVERSION = YES; 194 | CLANG_WARN_INFINITE_RECURSION = YES; 195 | CLANG_WARN_INT_CONVERSION = YES; 196 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 197 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 198 | CLANG_WARN_UNREACHABLE_CODE = YES; 199 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 200 | CODE_SIGN_IDENTITY = "-"; 201 | COPY_PHASE_STRIP = NO; 202 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 203 | ENABLE_NS_ASSERTIONS = NO; 204 | ENABLE_STRICT_OBJC_MSGSEND = YES; 205 | GCC_C_LANGUAGE_STANDARD = gnu99; 206 | GCC_NO_COMMON_BLOCKS = YES; 207 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 208 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 209 | GCC_WARN_UNDECLARED_SELECTOR = YES; 210 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 211 | GCC_WARN_UNUSED_FUNCTION = YES; 212 | GCC_WARN_UNUSED_VARIABLE = YES; 213 | MACOSX_DEPLOYMENT_TARGET = 10.12; 214 | MTL_ENABLE_DEBUG_INFO = NO; 215 | SDKROOT = macosx; 216 | }; 217 | name = Release; 218 | }; 219 | 1781A7671F5FF992008E6067 /* Debug */ = { 220 | isa = XCBuildConfiguration; 221 | buildSettings = { 222 | CLANG_CXX_LANGUAGE_STANDARD = "c++14"; 223 | PRODUCT_NAME = "$(TARGET_NAME)"; 224 | }; 225 | name = Debug; 226 | }; 227 | 1781A7681F5FF992008E6067 /* Release */ = { 228 | isa = XCBuildConfiguration; 229 | buildSettings = { 230 | CLANG_CXX_LANGUAGE_STANDARD = "c++14"; 231 | PRODUCT_NAME = "$(TARGET_NAME)"; 232 | }; 233 | name = Release; 234 | }; 235 | /* End XCBuildConfiguration section */ 236 | 237 | /* Begin XCConfigurationList section */ 238 | 1781A75A1F5FF992008E6067 /* Build configuration list for PBXProject "sample" */ = { 239 | isa = XCConfigurationList; 240 | buildConfigurations = ( 241 | 1781A7641F5FF992008E6067 /* Debug */, 242 | 1781A7651F5FF992008E6067 /* Release */, 243 | ); 244 | defaultConfigurationIsVisible = 0; 245 | defaultConfigurationName = Release; 246 | }; 247 | 1781A7661F5FF992008E6067 /* Build configuration list for PBXNativeTarget "sample" */ = { 248 | isa = XCConfigurationList; 249 | buildConfigurations = ( 250 | 1781A7671F5FF992008E6067 /* Debug */, 251 | 1781A7681F5FF992008E6067 /* Release */, 252 | ); 253 | defaultConfigurationIsVisible = 0; 254 | defaultConfigurationName = Release; 255 | }; 256 | /* End XCConfigurationList section */ 257 | }; 258 | rootObject = 1781A7571F5FF992008E6067 /* Project object */; 259 | } 260 | -------------------------------------------------------------------------------- /sample/sample.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {AC415D81-EA1D-43F3-A741-60ED85136D48} 23 | Win32Proj 24 | sample 25 | 26 | 27 | 28 | Application 29 | true 30 | v141 31 | Unicode 32 | 33 | 34 | Application 35 | false 36 | v141 37 | true 38 | Unicode 39 | 40 | 41 | Application 42 | true 43 | v141 44 | Unicode 45 | 46 | 47 | Application 48 | false 49 | v141 50 | true 51 | Unicode 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | true 73 | $(SolutionDir)$(Configuration)\sample\ 74 | $(Configuration)\sample\ 75 | 76 | 77 | true 78 | $(Platform)\$(Configuration)\sample\ 79 | $(SolutionDir)$(Platform)\$(Configuration)\sample\ 80 | 81 | 82 | false 83 | 84 | 85 | false 86 | 87 | 88 | 89 | 90 | 91 | Level3 92 | Disabled 93 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 94 | /std:c++14 %(AdditionalOptions) 95 | 96 | 97 | Console 98 | true 99 | 100 | 101 | 102 | 103 | 104 | 105 | Level3 106 | Disabled 107 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 108 | 109 | 110 | Console 111 | true 112 | 113 | 114 | 115 | 116 | Level3 117 | 118 | 119 | MaxSpeed 120 | true 121 | true 122 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 123 | 124 | 125 | Console 126 | true 127 | true 128 | true 129 | 130 | 131 | 132 | 133 | Level3 134 | 135 | 136 | MaxSpeed 137 | true 138 | true 139 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 140 | 141 | 142 | Console 143 | true 144 | true 145 | true 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /src/nullable.h: -------------------------------------------------------------------------------- 1 | 2 | // Nullable module for ORM Lite 3 | // ORM Lite - An ORM for SQLite in C++ 17 4 | // https://github.com/BOT-Man-JL/ORM-Lite 5 | // BOT Man, 2017 6 | 7 | 8 | #ifndef BOT_ORM_NULLABLE_H 9 | #define BOT_ORM_NULLABLE_H 10 | 11 | // std::nullptr_t 12 | #include 13 | 14 | // Nullable Template 15 | // https://stackoverflow.com/questions/2537942/nullable-values-in-c/28811646#28811646 16 | 17 | namespace BOT_ORM 18 | { 19 | template 20 | class Nullable 21 | { 22 | template 23 | friend bool operator== (const Nullable &op1, 24 | const Nullable &op2); 25 | template 26 | friend bool operator== (const Nullable &op, 27 | const T2 &value); 28 | template 29 | friend bool operator== (const T2 &value, 30 | const Nullable &op); 31 | template 32 | friend bool operator== (const Nullable &op, 33 | std::nullptr_t); 34 | template 35 | friend bool operator== (std::nullptr_t, 36 | const Nullable &op); 37 | public: 38 | // Default or Null Construction 39 | Nullable () 40 | : m_hasValue (false), m_value (T ()) 41 | {} 42 | Nullable (std::nullptr_t) 43 | : Nullable () 44 | {} 45 | 46 | // Null Assignment 47 | const Nullable & operator= (std::nullptr_t) 48 | { 49 | m_hasValue = false; 50 | m_value = T (); 51 | return *this; 52 | } 53 | 54 | // Value Construction 55 | template 56 | Nullable (const T2 &value) 57 | : m_hasValue (true), m_value (value) 58 | {} 59 | 60 | // Value Assignment 61 | template 62 | const Nullable & operator= (const T2 &value) 63 | { 64 | m_hasValue = true; 65 | m_value = value; 66 | return *this; 67 | } 68 | 69 | private: 70 | bool m_hasValue; 71 | T m_value; 72 | 73 | public: 74 | const T &Value () const 75 | { 76 | return m_value; 77 | } 78 | }; 79 | 80 | // == varialbe 81 | template 82 | inline bool operator== (const Nullable &op1, 83 | const Nullable &op2) 84 | { 85 | return op1.m_hasValue == op2.m_hasValue && 86 | (!op1.m_hasValue || op1.m_value == op2.m_value); 87 | } 88 | 89 | // == value 90 | template 91 | inline bool operator== (const Nullable &op, 92 | const T2 &value) 93 | { 94 | return op.m_hasValue && op.m_value == value; 95 | } 96 | template 97 | inline bool operator== (const T2 &value, 98 | const Nullable &op) 99 | { 100 | return op.m_hasValue && op.m_value == value; 101 | } 102 | 103 | // == nullptr 104 | template 105 | inline bool operator== (const Nullable &op, 106 | std::nullptr_t) 107 | { 108 | return !op.m_hasValue; 109 | } 110 | template 111 | inline bool operator== (std::nullptr_t, 112 | const Nullable &op) 113 | { 114 | return !op.m_hasValue; 115 | } 116 | } 117 | 118 | #endif // !BOT_ORM_NULLABLE_H 119 | -------------------------------------------------------------------------------- /src/ormlite.h: -------------------------------------------------------------------------------- 1 | 2 | // ORM Lite 3 | // An ORM for SQLite in C++ 14 4 | // https://github.com/BOT-Man-JL/ORM-Lite 5 | // BOT Man, 2017 6 | 7 | #ifndef BOT_ORM_H 8 | #define BOT_ORM_H 9 | 10 | // Container 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // Serialization 18 | #include 19 | 20 | // Type Traits 21 | #include 22 | 23 | // std::shared_ptr 24 | #include 25 | 26 | // for Field Name Extractor 27 | #include 28 | 29 | // for SQL Connector 30 | #include 31 | #include 32 | 33 | // SQLite 3 Dependency 34 | #include "sqlite3.h" 35 | 36 | // Nullable Module 37 | #include "nullable.h" 38 | 39 | // Public Macro 40 | 41 | #define ORMAP(_TABLE_NAME_, ...) \ 42 | private: \ 43 | friend class BOT_ORM_Impl::InjectionHelper; \ 44 | template \ 45 | inline decltype (auto) __Accept (FN fn) \ 46 | { return fn (__VA_ARGS__); } \ 47 | template \ 48 | inline decltype (auto) __Accept (FN fn) const \ 49 | { return fn (__VA_ARGS__); } \ 50 | constexpr static const char *__FieldNames = #__VA_ARGS__; \ 51 | constexpr static const char *__TableName = _TABLE_NAME_; \ 52 | 53 | // Private Macros 54 | 55 | #define NO_ORMAP "Please Inject the Class with 'ORMAP' first" 56 | #define BAD_TYPE "Only Support Integral, Floating Point and std::string" 57 | 58 | #define BAD_COLUMN_COUNT "Bad Column Count" 59 | #define NULL_DESERIALIZE "Get Null Value" 60 | 61 | #define NO_FIELD "No Such Field for current Extractor" 62 | #define NOT_SAME_TABLE "Fields are NOT from the Same Table" 63 | 64 | // Helpers 65 | 66 | namespace BOT_ORM_Impl 67 | { 68 | // Naive SQL Driver (Todo: Improved Later) 69 | 70 | class SQLConnector 71 | { 72 | public: 73 | SQLConnector (const std::string &fileName) 74 | { 75 | if (sqlite3_open (fileName.c_str (), &db) != SQLITE_OK) 76 | throw std::runtime_error ( 77 | std::string ("SQL error: Can't open database '") 78 | + sqlite3_errmsg (db) + "'"); 79 | } 80 | 81 | ~SQLConnector () 82 | { 83 | sqlite3_close (db); 84 | } 85 | 86 | void Execute (const std::string &cmd) 87 | { 88 | char *zErrMsg = 0; 89 | int rc = SQLITE_OK; 90 | 91 | for (size_t iTry = 0; iTry < MAX_TRIAL; iTry++) 92 | { 93 | rc = sqlite3_exec (db, cmd.c_str (), 0, 0, &zErrMsg); 94 | if (rc != SQLITE_BUSY) 95 | break; 96 | 97 | std::this_thread::sleep_for ( 98 | std::chrono::microseconds (20)); 99 | } 100 | 101 | if (rc != SQLITE_OK) 102 | { 103 | auto errStr = std::string ("SQL error: '") + zErrMsg 104 | + "' at '" + cmd + "'"; 105 | sqlite3_free (zErrMsg); 106 | throw std::runtime_error (errStr); 107 | } 108 | } 109 | 110 | void ExecuteCallback (const std::string &cmd, 111 | std::function 112 | callback) 113 | { 114 | char *zErrMsg = 0; 115 | int rc = SQLITE_OK; 116 | 117 | auto callbackParam = std::make_pair (&callback, std::string {}); 118 | 119 | for (size_t iTry = 0; iTry < MAX_TRIAL; iTry++) 120 | { 121 | rc = sqlite3_exec (db, cmd.c_str (), CallbackWrapper, 122 | &callbackParam, &zErrMsg); 123 | if (rc != SQLITE_BUSY) 124 | break; 125 | 126 | std::this_thread::sleep_for ( 127 | std::chrono::microseconds (20)); 128 | } 129 | 130 | if (rc == SQLITE_ABORT) 131 | { 132 | auto errStr = "SQL error: '" + callbackParam.second + 133 | "' at '" + cmd + "'"; 134 | sqlite3_free (zErrMsg); 135 | throw std::runtime_error (errStr); 136 | } 137 | else if (rc != SQLITE_OK) 138 | { 139 | auto errStr = std::string ("SQL error: '") + zErrMsg 140 | + "' at '" + cmd + "'"; 141 | sqlite3_free (zErrMsg); 142 | throw std::runtime_error (errStr); 143 | } 144 | } 145 | 146 | private: 147 | sqlite3 *db; 148 | constexpr static size_t MAX_TRIAL = 16; 149 | 150 | static int CallbackWrapper ( 151 | void *callbackParam, int argc, char **argv, char **) 152 | { 153 | auto pParam = static_cast *, 155 | std::string 156 | >*>(callbackParam); 157 | 158 | try 159 | { 160 | pParam->first->operator()(argc, argv); 161 | return 0; 162 | } 163 | catch (const std::exception &ex) 164 | { 165 | pParam->second = ex.what (); 166 | return 1; 167 | } 168 | } 169 | }; 170 | 171 | // Helper - Field Type Checker 172 | 173 | template 174 | struct TypeString 175 | { 176 | constexpr static const char *typeStr = 177 | (std::is_integral::value && 178 | !std::is_same::value && 179 | !std::is_same::value && 180 | !std::is_same::value && 181 | !std::is_same::value && 182 | !std::is_same::value) 183 | ? " integer not null" 184 | : (std::is_floating_point::value) 185 | ? " real not null" 186 | : (std::is_same::value) 187 | ? " text not null" 188 | : nullptr; 189 | 190 | static_assert (typeStr != nullptr, BAD_TYPE); 191 | }; 192 | 193 | template 194 | struct TypeString > 195 | { 196 | constexpr static const char *typeStr = 197 | (std::is_integral::value && 198 | !std::is_same::value && 199 | !std::is_same::value && 200 | !std::is_same::value && 201 | !std::is_same::value && 202 | !std::is_same::value) 203 | ? " integer" 204 | : (std::is_floating_point::value) 205 | ? " real" 206 | : (std::is_same::value) 207 | ? " text" 208 | : nullptr; 209 | 210 | static_assert (typeStr != nullptr, BAD_TYPE); 211 | }; 212 | 213 | // Serialization Helper 214 | 215 | struct SerializationHelper 216 | { 217 | template 218 | static inline 219 | std::enable_if_t::typeStr == nullptr, bool> 220 | Serialize (std::ostream &, const T &) 221 | {} 222 | template 223 | static inline 224 | std::enable_if_t::typeStr != nullptr, bool> 225 | Serialize (std::ostream &os, const T &value) 226 | { 227 | os << value; 228 | return true; 229 | } 230 | 231 | static inline bool Serialize (std::ostream &os, 232 | const std::string &value) 233 | { 234 | os << "'" << value << "'"; 235 | return true; 236 | } 237 | 238 | template 239 | static inline bool Serialize ( 240 | std::ostream &os, 241 | const BOT_ORM::Nullable &value) 242 | { 243 | if (value == nullptr) 244 | return false; 245 | return Serialize (os, value.Value ()); 246 | } 247 | }; 248 | 249 | // Deserialization Helper 250 | 251 | struct DeserializationHelper 252 | { 253 | template 254 | static inline std::enable_if_t::typeStr == nullptr> 255 | Deserialize (T &, const char *) 256 | {} 257 | template 258 | static inline std::enable_if_t::typeStr != nullptr> 259 | Deserialize (T &property, const char *value) 260 | { 261 | if (value) std::istringstream { value } >> property; 262 | else throw std::runtime_error (NULL_DESERIALIZE); 263 | } 264 | 265 | static inline void Deserialize (std::string &property, 266 | const char *value) 267 | { 268 | if (value) property = value; 269 | else throw std::runtime_error (NULL_DESERIALIZE); 270 | } 271 | 272 | template 273 | static inline void Deserialize ( 274 | BOT_ORM::Nullable &property, const char *value) 275 | { 276 | if (value) 277 | { 278 | T val; 279 | Deserialize (val, value); 280 | property = val; 281 | } 282 | else 283 | property = nullptr; 284 | } 285 | }; 286 | 287 | // Injection Helper 288 | 289 | class InjectionHelper 290 | { 291 | static std::vector ExtractFieldName (std::string input) 292 | { 293 | std::vector ret; 294 | std::string tmpStr; 295 | 296 | for (char ch : std::move (input) + ",") 297 | { 298 | if (isalnum (ch) || ch == '_') 299 | tmpStr += ch; 300 | else if (ch == ',') 301 | { 302 | if (!tmpStr.empty ()) 303 | ret.push_back (tmpStr); 304 | tmpStr.clear (); 305 | } 306 | } 307 | return ret; 308 | }; 309 | 310 | public: 311 | // Checking Injection 312 | template class HasInjected 313 | { 314 | template struct make_void { using type = void; }; 315 | template 316 | using void_t = typename make_void<_Types...>::type; 317 | 318 | template > 319 | struct Test : std::false_type {}; 320 | template 321 | struct Test > 322 | : std::true_type 323 | {}; 324 | 325 | public: 326 | static constexpr bool value = Test::value; 327 | 328 | static_assert (value, NO_ORMAP); 329 | }; 330 | 331 | // Proxy Function 332 | template 333 | static inline decltype (auto) Visit (C &obj, Fn fn) 334 | { 335 | return obj.__Accept (fn); 336 | } 337 | 338 | // Field Name Proxy 339 | template 340 | static inline const std::vector &FieldNames (const C &) 341 | { 342 | static const auto fieldNames = ExtractFieldName (C::__FieldNames); 343 | return fieldNames; 344 | } 345 | 346 | // Table Name Proxy 347 | template 348 | static inline const std::string &TableName (const C &) 349 | { 350 | static const std::string tableName { C::__TableName }; 351 | return tableName; 352 | } 353 | }; 354 | 355 | // Unpacking Tricks :-) 356 | // http://stackoverflow.com/questions/26902633/how-to-iterate-over-a-tuple-in-c-11/26902803#26902803 357 | // - To avoid the unspecified order, 358 | // brace-enclosed initializer lists can be used, 359 | // which guarantee strict left-to-right order of evaluation. 360 | // - To avoid the need for a not void return type, 361 | // the comma operator can be used to 362 | // always yield 1 in each expansion element. 363 | 364 | using Expander = int[]; 365 | } 366 | 367 | namespace BOT_ORM 368 | { 369 | namespace Expression 370 | { 371 | // SetExpr 372 | 373 | struct SetExpr 374 | { 375 | SetExpr (std::string field_op_val) 376 | : _expr { std::move (field_op_val) } 377 | {} 378 | 379 | const std::string &ToString () const 380 | { 381 | return _expr; 382 | } 383 | 384 | inline SetExpr operator && (const SetExpr &rhs) const 385 | { 386 | return SetExpr { _expr + "," + rhs._expr }; 387 | } 388 | 389 | private: 390 | std::string _expr; 391 | }; 392 | 393 | // Selectable 394 | 395 | template 396 | struct Selectable 397 | { 398 | std::string fieldName; 399 | const std::string *tableName; 400 | 401 | Selectable (std::string _fieldName, 402 | const std::string *_tableName) 403 | : fieldName (std::move (_fieldName)), tableName (_tableName) 404 | {} 405 | }; 406 | 407 | // Field : Selectable 408 | 409 | template 410 | struct Field : public Selectable 411 | { 412 | Field (std::string _fieldName, 413 | const std::string *_tableName) 414 | : Selectable (std::move (_fieldName), _tableName) {} 415 | 416 | inline SetExpr operator = (T value) 417 | { 418 | std::ostringstream os; 419 | BOT_ORM_Impl::SerializationHelper:: 420 | Serialize (os << this->fieldName << "=", value); 421 | return SetExpr { os.str () }; 422 | } 423 | }; 424 | 425 | // Nullable Field : Field : Selectable 426 | 427 | template 428 | struct NullableField : public Field 429 | { 430 | NullableField (std::string _fieldName, 431 | const std::string *_tableName) 432 | : Field (std::move (_fieldName), _tableName) {} 433 | 434 | inline SetExpr operator = (T value) 435 | { 436 | std::ostringstream os; 437 | BOT_ORM_Impl::SerializationHelper:: 438 | Serialize (os << this->fieldName << "=", value); 439 | return SetExpr { os.str () }; 440 | } 441 | 442 | inline SetExpr operator = (std::nullptr_t) 443 | { 444 | return SetExpr { this->fieldName + "=null" }; 445 | } 446 | }; 447 | 448 | // Aggregate Function : Selectable 449 | 450 | template 451 | struct Aggregate : public Selectable 452 | { 453 | Aggregate (std::string function) 454 | : Selectable (std::move (function), nullptr) {} 455 | 456 | Aggregate (std::string function, const Field &field) 457 | : Selectable (std::move (function) + "(" + 458 | *(field.tableName) + "." + 459 | field.fieldName + ")", nullptr) 460 | {} 461 | }; 462 | 463 | // Expr 464 | 465 | struct Expr 466 | { 467 | template 468 | Expr (const Selectable &field, std::string op_val) 469 | : _exprs { { field.fieldName + op_val, field.tableName } } 470 | {} 471 | 472 | template 473 | Expr (const Selectable &field, std::string op, T value) 474 | { 475 | std::ostringstream os; 476 | BOT_ORM_Impl::SerializationHelper:: 477 | Serialize (os << field.fieldName << op, value); 478 | _exprs.emplace_back (os.str (), field.tableName); 479 | } 480 | 481 | template 482 | Expr (const Field &field1, 483 | std::string op, 484 | const Field &field2) 485 | : _exprs 486 | { 487 | { field1.fieldName, field1.tableName }, 488 | { std::move (op), nullptr }, 489 | { field2.fieldName, field2.tableName } 490 | } 491 | {} 492 | 493 | std::string ToString () const 494 | { 495 | std::ostringstream out; 496 | for (const auto &expr : _exprs) 497 | { 498 | if (expr.second != nullptr) 499 | out << *(expr.second) << "."; 500 | out << expr.first; 501 | } 502 | return out.str (); 503 | } 504 | 505 | inline Expr operator && (const Expr &rhs) const 506 | { 507 | return And_Or (rhs, " and "); 508 | } 509 | inline Expr operator || (const Expr &rhs) const 510 | { 511 | return And_Or (rhs, " or "); 512 | } 513 | 514 | private: 515 | std::list> _exprs; 516 | 517 | Expr And_Or (const Expr &rhs, std::string logOp) const 518 | { 519 | auto ret = *this; 520 | auto rigthExprs = rhs._exprs; 521 | ret._exprs.emplace_front ("(", nullptr); 522 | ret._exprs.emplace_back (std::move (logOp), nullptr); 523 | ret._exprs.splice (ret._exprs.cend (), 524 | std::move (rigthExprs)); 525 | ret._exprs.emplace_back (")", nullptr); 526 | return ret; 527 | } 528 | }; 529 | 530 | // Field / Aggregate ? Value 531 | 532 | template 533 | inline Expr operator == (const Selectable &op, T value) 534 | { 535 | return Expr (op, "=", std::move (value)); 536 | } 537 | 538 | template 539 | inline Expr operator != (const Selectable &op, T value) 540 | { 541 | return Expr (op, "!=", std::move (value)); 542 | } 543 | 544 | template 545 | inline Expr operator > (const Selectable &op, T value) 546 | { 547 | return Expr (op, ">", std::move (value)); 548 | } 549 | 550 | template 551 | inline Expr operator >= (const Selectable &op, T value) 552 | { 553 | return Expr (op, ">=", std::move (value)); 554 | } 555 | 556 | template 557 | inline Expr operator < (const Selectable &op, T value) 558 | { 559 | return Expr (op, "<", std::move (value)); 560 | } 561 | 562 | template 563 | inline Expr operator <= (const Selectable &op, T value) 564 | { 565 | return Expr (op, "<=", std::move (value)); 566 | } 567 | 568 | // Field ? Field 569 | 570 | template 571 | inline Expr operator == (const Field &op1, const Field &op2) 572 | { 573 | return Expr { op1 , "=", op2 }; 574 | } 575 | 576 | template 577 | inline Expr operator != (const Field &op1, const Field &op2) 578 | { 579 | return Expr { op1, "!=", op2 }; 580 | } 581 | 582 | template 583 | inline Expr operator > (const Field &op1, const Field &op2) 584 | { 585 | return Expr { op1 , ">", op2 }; 586 | } 587 | 588 | template 589 | inline Expr operator >= (const Field &op1, const Field &op2) 590 | { 591 | return Expr { op1, ">=", op2 }; 592 | } 593 | 594 | template 595 | inline Expr operator < (const Field &op1, const Field &op2) 596 | { 597 | return Expr { op1 , "<", op2 }; 598 | } 599 | 600 | template 601 | inline Expr operator <= (const Field &op1, const Field &op2) 602 | { 603 | return Expr { op1, "<=", op2 }; 604 | } 605 | 606 | // Nullable Field == / != nullptr 607 | 608 | template 609 | inline Expr operator == (const NullableField &op, std::nullptr_t) 610 | { 611 | return Expr { op, " is null" }; 612 | } 613 | 614 | template 615 | inline Expr operator != (const NullableField &op, std::nullptr_t) 616 | { 617 | return Expr { op, " is not null" }; 618 | } 619 | 620 | // String Field & / | std::string 621 | 622 | inline Expr operator & (const Field &field, 623 | std::string val) 624 | { 625 | return Expr (field, " like ", std::move (val)); 626 | } 627 | 628 | inline Expr operator | (const Field &field, 629 | std::string val) 630 | { 631 | return Expr (field, " not like ", std::move (val)); 632 | } 633 | 634 | // Aggregate Function Helpers 635 | 636 | inline auto Count () 637 | { 638 | return Aggregate { "count (*)" }; 639 | } 640 | 641 | template 642 | inline auto Count (const Field &field) 643 | { 644 | return Aggregate { "count", field }; 645 | } 646 | 647 | template 648 | inline auto Sum (const Field &field) 649 | { 650 | return Aggregate { "sum", field }; 651 | } 652 | 653 | template 654 | inline auto Avg (const Field &field) 655 | { 656 | return Aggregate { "avg", field }; 657 | } 658 | 659 | template 660 | inline auto Max (const Field &field) 661 | { 662 | return Aggregate { "max", field }; 663 | } 664 | 665 | template 666 | inline auto Min (const Field &field) 667 | { 668 | return Aggregate { "min", field }; 669 | } 670 | } 671 | 672 | class ORMapper; 673 | 674 | class Constraint 675 | { 676 | protected: 677 | std::string constraint; 678 | std::string field; 679 | 680 | Constraint (std::string &&_constraint, 681 | std::string _field = std::string {}) 682 | : constraint (_constraint), field (std::move (_field)) 683 | {} 684 | 685 | friend class ORMapper; 686 | 687 | public: 688 | struct CompositeField 689 | { 690 | std::string fieldName; 691 | const std::string *tableName; 692 | 693 | template 694 | CompositeField (const Fields & ... args) 695 | : tableName (nullptr) 696 | { 697 | (void) BOT_ORM_Impl::Expander 698 | { 699 | 0, (Extract (args), 0)... 700 | }; 701 | } 702 | 703 | private: 704 | template 705 | void Extract (const Expression::Field &field) 706 | { 707 | if (tableName != nullptr && tableName != field.tableName) 708 | throw std::runtime_error (NOT_SAME_TABLE); 709 | tableName = field.tableName; 710 | if (fieldName.empty ()) 711 | fieldName = field.fieldName; 712 | else 713 | fieldName = field.fieldName + "," + fieldName; 714 | } 715 | }; 716 | 717 | template 718 | static inline Constraint Default ( 719 | const Expression::Field &field, 720 | const T &value) 721 | { 722 | std::ostringstream os; 723 | BOT_ORM_Impl::SerializationHelper:: 724 | Serialize (os << " default ", value); 725 | return Constraint { os.str (), field.fieldName }; 726 | } 727 | 728 | static inline Constraint Check ( 729 | const Expression::Expr &expr) 730 | { 731 | return Constraint { "check (" + expr.ToString () + ")" }; 732 | } 733 | 734 | template 735 | static inline Constraint Unique ( 736 | const Expression::Field &field) 737 | { 738 | return Constraint { "unique (" + field.fieldName + ")" }; 739 | } 740 | 741 | static inline Constraint Unique ( 742 | const CompositeField &fields) 743 | { 744 | return Constraint { "unique (" + fields.fieldName + ")" }; 745 | } 746 | 747 | template 748 | static inline Constraint Reference ( 749 | const Expression::Field &field, 750 | const Expression::Field &refered) 751 | { 752 | return Constraint { 753 | std::string ("foreign key (") + field.fieldName + 754 | ") references " + *(refered.tableName) + 755 | "(" + refered.fieldName + ")" }; 756 | } 757 | 758 | static inline Constraint Reference ( 759 | const CompositeField &field, 760 | const CompositeField &refered) 761 | { 762 | return Constraint { 763 | std::string ("foreign key (") + field.fieldName + 764 | ") references " + *(refered.tableName) + 765 | "(" + refered.fieldName + ")" }; 766 | } 767 | }; 768 | 769 | template 770 | class Queryable; 771 | } 772 | 773 | namespace BOT_ORM_Impl 774 | { 775 | // Why Remove QueryableHelper from Query? 776 | // Query is a template but the Helper can be shared 777 | 778 | class QueryableHelper 779 | { 780 | template 781 | using Nullable = BOT_ORM::Nullable; 782 | 783 | template 784 | using Selectable = BOT_ORM::Expression::Selectable; 785 | 786 | protected: 787 | template 788 | friend class BOT_ORM::Queryable; 789 | 790 | // #1 Tuple Visitor 791 | 792 | // Apply 'Fn' to each of element of Tuple 793 | template 794 | static inline void TupleVisit_Impl ( 795 | TupleType &tuple, Fn fn, std::index_sequence) 796 | { 797 | (void) BOT_ORM_Impl::Expander 798 | { 799 | 0, ((void) fn (std::get (tuple)), 0)... 800 | }; 801 | } 802 | 803 | // Produce the 'index_sequence' for 'tuple' 804 | template 805 | static inline void TupleVisit (TupleType &tuple, Fn fn) 806 | { 807 | constexpr auto size = std::tuple_size::value; 808 | return TupleVisit_Impl (tuple, fn, 809 | std::make_index_sequence {}); 810 | } 811 | 812 | // #2 Join To Tuple 813 | 814 | // Type To Nullable 815 | // Get Nullable Type Wrappers for Non-nullable Types 816 | template struct TypeToNullable 817 | { 818 | using type = Nullable; 819 | }; 820 | template struct TypeToNullable > 821 | { 822 | using type = Nullable; 823 | }; 824 | 825 | // Fields To Nullable 826 | // Apply 'TypeToNullable' to each element of Fields 827 | template 828 | static inline auto FieldsToNullable (Args...) 829 | { 830 | // Unpacking Tricks :-) 831 | // Expand each of 'Args' 832 | // with 'TypeToNullable_t<...>' as a sequence 833 | return std::tuple< 834 | typename TypeToNullable::type... 835 | > {}; 836 | } 837 | 838 | // QueryResult To Nullable 839 | template 840 | static inline auto QueryResultToTuple (const C &arg) 841 | { 842 | return BOT_ORM_Impl::InjectionHelper::Visit ( 843 | arg, [] (auto ... args) 844 | { 845 | return FieldsToNullable (args...); 846 | }); 847 | } 848 | 849 | template 850 | static inline auto QueryResultToTuple ( 851 | const std::tuple& t) 852 | { 853 | // FieldsToNullable is not necessary: Nullable already 854 | return t; 855 | } 856 | 857 | // Construct Tuple from QueryResult List 858 | template 859 | static inline auto JoinToTuple (const Args & ... args) 860 | { 861 | // Unpacking Tricks :-) 862 | return decltype (std::tuple_cat ( 863 | QueryResultToTuple (args)... 864 | )) {}; 865 | } 866 | 867 | // #3 Select To Tuple 868 | 869 | // Selectable To Tuple 870 | template 871 | static inline auto SelectableToTuple (const Selectable &) 872 | { 873 | // Notes: Only 'const Selectable &' will overload... 874 | return Nullable {}; 875 | } 876 | 877 | // Construct Tuple from Selectable List 878 | template 879 | static inline auto SelectToTuple (const Args & ... args) 880 | { 881 | // Unpacking Tricks :-) 882 | return std::tuple < 883 | decltype (SelectableToTuple (args))... 884 | > {}; 885 | } 886 | 887 | // #4 Field To SQL 888 | 889 | // Return Field Strings for GroupBy, OrderBy and Select 890 | template 891 | static inline std::string FieldToSql (const Selectable &op) 892 | { 893 | if (op.tableName) return (*op.tableName) + "." + op.fieldName; 894 | else return op.fieldName; 895 | } 896 | template 897 | static inline std::string FieldToSql (const Selectable &arg, 898 | const Args & ... args) 899 | { 900 | return FieldToSql (arg) + "," + FieldToSql (args...); 901 | } 902 | }; 903 | } 904 | 905 | namespace BOT_ORM 906 | { 907 | // Queryable 908 | 909 | template 910 | class Queryable 911 | { 912 | template 913 | using HasInjected = 914 | BOT_ORM_Impl::InjectionHelper::HasInjected; 915 | 916 | protected: 917 | std::shared_ptr _connector; 918 | QueryResult _queryHelper; 919 | 920 | std::string _sqlFrom; 921 | std::string _sqlSelect; 922 | std::string _sqlTarget; 923 | 924 | std::string _sqlWhere; 925 | std::string _sqlGroupBy; 926 | std::string _sqlHaving; 927 | 928 | std::string _sqlOrderBy; 929 | std::string _sqlLimit; 930 | std::string _sqlOffset; 931 | 932 | Queryable ( 933 | std::shared_ptr connector, 934 | QueryResult queryHelper, 935 | std::string sqlFrom, 936 | std::string sqlSelect = "select ", 937 | std::string sqlTarget = "*", 938 | std::string sqlWhere = std::string {}, 939 | std::string sqlGroupBy = std::string {}, 940 | std::string sqlHaving = std::string {}, 941 | std::string sqlOrderBy = std::string {}, 942 | std::string sqlLimit = std::string {}, 943 | std::string sqlOffset = std::string {}) 944 | : 945 | _connector (std::move (connector)), 946 | _queryHelper (std::move (queryHelper)), 947 | _sqlFrom (std::move (sqlFrom)), 948 | _sqlSelect (std::move (sqlSelect)), 949 | _sqlTarget (std::move (sqlTarget)), 950 | _sqlWhere (std::move (sqlWhere)), 951 | _sqlGroupBy (std::move (sqlGroupBy)), 952 | _sqlHaving (std::move (sqlHaving)), 953 | _sqlOrderBy (std::move (sqlOrderBy)), 954 | _sqlLimit (std::move (sqlLimit)), 955 | _sqlOffset (std::move (sqlOffset)) 956 | {} 957 | 958 | template friend class Queryable; 959 | friend class ORMapper; 960 | 961 | public: 962 | // Distinct 963 | inline Queryable Distinct () const & 964 | { 965 | auto ret = *this; 966 | ret._sqlSelect = "select distinct "; 967 | return ret; 968 | } 969 | inline Queryable Distinct () && 970 | { 971 | (*this)._sqlSelect = "select distinct "; 972 | return std::move (*this); 973 | } 974 | 975 | // Where 976 | inline Queryable Where (const Expression::Expr &expr) const & 977 | { 978 | auto ret = *this; 979 | ret._sqlWhere = " where (" + expr.ToString () + ")"; 980 | return ret; 981 | } 982 | inline Queryable Where (const Expression::Expr &expr) && 983 | { 984 | (*this)._sqlWhere = " where (" + expr.ToString () + ")"; 985 | return std::move (*this); 986 | } 987 | 988 | // Group By 989 | template 990 | inline Queryable GroupBy (const Expression::Field &field) const & 991 | { 992 | auto ret = *this; 993 | ret._sqlGroupBy = " group by " + 994 | BOT_ORM_Impl::QueryableHelper::FieldToSql (field); 995 | return ret; 996 | } 997 | template 998 | inline Queryable GroupBy (const Expression::Field &field) && 999 | { 1000 | (*this)._sqlGroupBy = " group by " + 1001 | BOT_ORM_Impl::QueryableHelper::FieldToSql (field); 1002 | return std::move (*this); 1003 | } 1004 | 1005 | // Having 1006 | inline Queryable Having (const Expression::Expr &expr) const & 1007 | { 1008 | auto ret = *this; 1009 | ret._sqlHaving = " having " + expr.ToString (); 1010 | return ret; 1011 | } 1012 | inline Queryable Having (const Expression::Expr &expr) && 1013 | { 1014 | (*this)._sqlHaving = " having " + expr.ToString (); 1015 | return std::move (*this); 1016 | } 1017 | 1018 | // Limit 1019 | inline Queryable Take (size_t count) const & 1020 | { 1021 | auto ret = *this; 1022 | ret._sqlLimit = " limit " + std::to_string (count); 1023 | return ret; 1024 | } 1025 | inline Queryable Take (size_t count) && 1026 | { 1027 | (*this)._sqlLimit = " limit " + std::to_string (count); 1028 | return std::move (*this); 1029 | } 1030 | 1031 | // Offset 1032 | inline Queryable Skip (size_t count) const & 1033 | { 1034 | auto ret = *this; 1035 | if (ret._sqlLimit.empty ()) 1036 | ret._sqlLimit = " limit ~0"; // ~0 is a trick :-) 1037 | ret._sqlOffset = " offset " + std::to_string (count); 1038 | return ret; 1039 | } 1040 | inline Queryable Skip (size_t count) && 1041 | { 1042 | if ((*this)._sqlLimit.empty ()) 1043 | (*this)._sqlLimit = " limit ~0"; // ~0 is a trick :-) 1044 | (*this)._sqlOffset = " offset " + std::to_string (count); 1045 | return std::move (*this); 1046 | } 1047 | 1048 | // Order By 1049 | template 1050 | inline Queryable OrderBy ( 1051 | const Expression::Field &field) const & 1052 | { 1053 | auto ret = *this; 1054 | if (ret._sqlOrderBy.empty ()) 1055 | ret._sqlOrderBy = " order by " + 1056 | BOT_ORM_Impl::QueryableHelper::FieldToSql (field); 1057 | else 1058 | ret._sqlOrderBy += "," + 1059 | BOT_ORM_Impl::QueryableHelper::FieldToSql (field); 1060 | return ret; 1061 | } 1062 | template 1063 | inline Queryable OrderBy ( 1064 | const Expression::Field &field) && 1065 | { 1066 | if ((*this)._sqlOrderBy.empty ()) 1067 | (*this)._sqlOrderBy = " order by " + 1068 | BOT_ORM_Impl::QueryableHelper::FieldToSql (field); 1069 | else 1070 | (*this)._sqlOrderBy += "," + 1071 | BOT_ORM_Impl::QueryableHelper::FieldToSql (field); 1072 | return std::move (*this); 1073 | } 1074 | 1075 | // Order By Desc 1076 | template 1077 | inline Queryable OrderByDescending ( 1078 | const Expression::Field &field) const & 1079 | { 1080 | auto ret = *this; 1081 | if (ret._sqlOrderBy.empty ()) 1082 | ret._sqlOrderBy = " order by " + 1083 | BOT_ORM_Impl::QueryableHelper::FieldToSql (field) + " desc"; 1084 | else 1085 | ret._sqlOrderBy += "," + 1086 | BOT_ORM_Impl::QueryableHelper::FieldToSql (field) + " desc"; 1087 | return ret; 1088 | } 1089 | template 1090 | inline Queryable OrderByDescending ( 1091 | const Expression::Field &field) && 1092 | { 1093 | if ((*this)._sqlOrderBy.empty ()) 1094 | (*this)._sqlOrderBy = " order by " + 1095 | BOT_ORM_Impl::QueryableHelper::FieldToSql (field) + " desc"; 1096 | else 1097 | (*this)._sqlOrderBy += "," + 1098 | BOT_ORM_Impl::QueryableHelper::FieldToSql (field) + " desc"; 1099 | return std::move (*this); 1100 | } 1101 | 1102 | // Select 1103 | template 1104 | inline auto Select (const Args & ... args) const 1105 | { 1106 | return _NewQuery ( 1107 | BOT_ORM_Impl::QueryableHelper::FieldToSql (args...), 1108 | _sqlFrom, 1109 | BOT_ORM_Impl::QueryableHelper::SelectToTuple (args...) 1110 | ); 1111 | } 1112 | 1113 | // Join 1114 | template 1115 | inline auto Join (const C &, 1116 | const Expression::Expr &, 1117 | std::enable_if_t< 1118 | !HasInjected::value> 1119 | * = nullptr) const 1120 | {} 1121 | template 1122 | inline auto Join (const C &queryHelper2, 1123 | const Expression::Expr &onExpr, 1124 | std::enable_if_t< 1125 | HasInjected::value> 1126 | * = nullptr) const 1127 | { 1128 | return _NewJoinQuery (queryHelper2, onExpr, " join "); 1129 | } 1130 | 1131 | // Left Join 1132 | template 1133 | inline auto LeftJoin (const C &, 1134 | const Expression::Expr &, 1135 | std::enable_if_t< 1136 | !HasInjected::value> 1137 | * = nullptr) const 1138 | {} 1139 | template 1140 | inline auto LeftJoin (const C &queryHelper2, 1141 | const Expression::Expr &onExpr, 1142 | std::enable_if_t< 1143 | HasInjected::value> 1144 | * = nullptr) const 1145 | { 1146 | return _NewJoinQuery (queryHelper2, onExpr, " left join "); 1147 | } 1148 | 1149 | // Compound Select 1150 | inline Queryable Union (const Queryable &queryable) const 1151 | { 1152 | return _NewCompoundQuery (queryable, " union "); 1153 | } 1154 | inline Queryable UnionAll (Queryable queryable) const 1155 | { 1156 | return _NewCompoundQuery (queryable, " union all "); 1157 | } 1158 | inline Queryable Intersect (Queryable queryable) const 1159 | { 1160 | return _NewCompoundQuery (queryable, " intersect "); 1161 | } 1162 | inline Queryable Except (Queryable queryable) const 1163 | { 1164 | return _NewCompoundQuery (queryable, " excpet "); 1165 | } 1166 | 1167 | // Get Result 1168 | template 1169 | Nullable Aggregate (const Expression::Aggregate &agg) const 1170 | { 1171 | Nullable ret; 1172 | _connector->ExecuteCallback (_sqlSelect + agg.fieldName + 1173 | _GetFromSql () + _GetLimit () + ";", 1174 | [&] (int argc, char **argv) 1175 | { 1176 | if (argc != 1) 1177 | throw std::runtime_error (BAD_COLUMN_COUNT); 1178 | 1179 | BOT_ORM_Impl::DeserializationHelper:: 1180 | Deserialize (ret, argv[0]); 1181 | }); 1182 | return ret; 1183 | } 1184 | 1185 | std::vector ToVector () const 1186 | { 1187 | std::vector ret; 1188 | _Select (_queryHelper, ret); 1189 | return ret; 1190 | } 1191 | std::list ToList () const 1192 | { 1193 | std::list ret; 1194 | _Select (_queryHelper, ret); 1195 | return ret; 1196 | } 1197 | 1198 | protected: 1199 | // Return FROM part for Query 1200 | inline std::string _GetFromSql () const 1201 | { 1202 | return _sqlFrom + _sqlWhere + _sqlGroupBy + _sqlHaving; 1203 | } 1204 | 1205 | // Return ORDER BY & LIMIT part for Query 1206 | inline std::string _GetLimit () const 1207 | { 1208 | return _sqlOrderBy + _sqlLimit + _sqlOffset; 1209 | } 1210 | 1211 | // Return a new Queryable Object 1212 | template 1213 | inline Queryable> _NewQuery ( 1214 | std::string sqlTarget, 1215 | std::string sqlFrom, 1216 | std::tuple &&newQueryHelper) const 1217 | { 1218 | return Queryable> ( 1219 | _connector, newQueryHelper, 1220 | std::move (sqlFrom), 1221 | _sqlSelect, std::move (sqlTarget), 1222 | _sqlWhere, _sqlGroupBy, _sqlHaving, 1223 | _sqlOrderBy, _sqlLimit, _sqlOffset); 1224 | } 1225 | 1226 | // Return a new Join Queryable Object 1227 | template 1228 | inline auto _NewJoinQuery (const C &queryHelper2, 1229 | const Expression::Expr &onExpr, 1230 | std::string joinStr) const 1231 | { 1232 | return _NewQuery ( 1233 | _sqlTarget, 1234 | _sqlFrom + std::move (joinStr) + 1235 | BOT_ORM_Impl::InjectionHelper::TableName (queryHelper2) + 1236 | " on " + onExpr.ToString (), 1237 | BOT_ORM_Impl::QueryableHelper::JoinToTuple ( 1238 | _queryHelper, queryHelper2)); 1239 | } 1240 | 1241 | // Return a new Compound Queryable Object 1242 | Queryable _NewCompoundQuery (const Queryable &queryable, 1243 | std::string compoundStr) const 1244 | { 1245 | auto ret = *this; 1246 | ret._sqlFrom = ret._GetFromSql () + 1247 | std::move (compoundStr) + 1248 | queryable._sqlSelect + queryable._sqlTarget + 1249 | queryable._GetFromSql (); 1250 | ret._sqlWhere.clear (); 1251 | ret._sqlGroupBy.clear (); 1252 | ret._sqlHaving.clear (); 1253 | return ret; 1254 | } 1255 | 1256 | // Select for Normal Objects 1257 | template 1258 | inline void _Select (const C &, Out &out) const 1259 | { 1260 | auto copy = _queryHelper; 1261 | _connector->ExecuteCallback (_sqlSelect + _sqlTarget + 1262 | _GetFromSql () + _GetLimit () + ";", 1263 | [&] (int argc, char **argv) 1264 | { 1265 | BOT_ORM_Impl::InjectionHelper::Visit ( 1266 | copy, [argc] (auto & ... args) 1267 | { 1268 | if (sizeof... (args) != argc) 1269 | throw std::runtime_error (BAD_COLUMN_COUNT); 1270 | }); 1271 | 1272 | BOT_ORM_Impl::InjectionHelper::Visit ( 1273 | copy, [argv] (auto & ... args) 1274 | { 1275 | size_t index = 0; 1276 | (void) BOT_ORM_Impl::Expander 1277 | { 1278 | 0, (BOT_ORM_Impl::DeserializationHelper:: 1279 | Deserialize (args, argv[index++]), 0)... 1280 | }; 1281 | }); 1282 | out.push_back (copy); 1283 | }); 1284 | } 1285 | 1286 | // Select for Tuples 1287 | template 1288 | inline void _Select (const std::tuple &, Out &out) const 1289 | { 1290 | auto copy = _queryHelper; 1291 | _connector->ExecuteCallback (_sqlSelect + _sqlTarget + 1292 | _GetFromSql () + _GetLimit () + ";", 1293 | [&] (int argc, char **argv) 1294 | { 1295 | if (sizeof... (Args) != argc) 1296 | throw std::runtime_error (BAD_COLUMN_COUNT); 1297 | 1298 | size_t index = 0; 1299 | BOT_ORM_Impl::QueryableHelper::TupleVisit ( 1300 | copy, [argv, &index] (auto &val) 1301 | { 1302 | BOT_ORM_Impl::DeserializationHelper:: 1303 | Deserialize (val, argv[index++]); 1304 | }); 1305 | out.push_back (copy); 1306 | }); 1307 | } 1308 | }; 1309 | 1310 | // ORMapper 1311 | 1312 | class ORMapper 1313 | { 1314 | template 1315 | using HasInjected = 1316 | BOT_ORM_Impl::InjectionHelper::HasInjected; 1317 | 1318 | public: 1319 | ORMapper (const std::string &connectionString) 1320 | : _connector ( 1321 | std::make_shared ( 1322 | connectionString)) 1323 | { 1324 | _connector->Execute ("PRAGMA foreign_keys = ON;"); 1325 | } 1326 | 1327 | template 1328 | void Transaction (Fn fn) 1329 | { 1330 | try 1331 | { 1332 | _connector->Execute ("begin transaction;"); 1333 | fn (); 1334 | _connector->Execute ("commit transaction;"); 1335 | } 1336 | catch (...) 1337 | { 1338 | _connector->Execute ("rollback transaction;"); 1339 | throw; 1340 | } 1341 | } 1342 | 1343 | template 1344 | std::enable_if_t::value> 1345 | CreateTbl (const C &, const Args & ...) 1346 | {} 1347 | template 1348 | std::enable_if_t::value> 1349 | CreateTbl (const C &entity, 1350 | const Args & ... constraints) 1351 | { 1352 | const auto &fieldNames = 1353 | BOT_ORM_Impl::InjectionHelper::FieldNames (entity); 1354 | std::unordered_map fieldFixes; 1355 | 1356 | auto addTypeStr = [&fieldNames, &fieldFixes] ( 1357 | const auto &arg, size_t index) 1358 | { 1359 | // Why addTypeStr? 1360 | // Walkaround the 'undefined reference' in gcc/clang 1361 | constexpr const char *typeStr = BOT_ORM_Impl::TypeString< 1362 | std::remove_cv_t> 1363 | >::typeStr; 1364 | fieldFixes.emplace (fieldNames[index], typeStr); 1365 | }; 1366 | (void) addTypeStr; 1367 | 1368 | BOT_ORM_Impl::InjectionHelper::Visit ( 1369 | entity, [&addTypeStr] (const auto & ... args) 1370 | { 1371 | size_t index = 0; 1372 | (void) BOT_ORM_Impl::Expander 1373 | { 1374 | 0, (addTypeStr (args, index++), 0)... 1375 | }; 1376 | }); 1377 | fieldFixes[fieldNames[0]] += " primary key"; 1378 | 1379 | std::string tableFixes; 1380 | _GetConstraints (tableFixes, fieldFixes, constraints...); 1381 | 1382 | std::string strFmt; 1383 | for (const auto &field : fieldNames) 1384 | strFmt += field + fieldFixes[field] + ","; 1385 | strFmt += std::move (tableFixes); 1386 | strFmt.pop_back (); 1387 | 1388 | _connector->Execute ( 1389 | "create table " + 1390 | BOT_ORM_Impl::InjectionHelper::TableName (entity) + 1391 | "(" + strFmt + ");"); 1392 | } 1393 | 1394 | template 1395 | std::enable_if_t::value> 1396 | DropTbl (const C &) 1397 | {} 1398 | template 1399 | std::enable_if_t::value> 1400 | DropTbl (const C &entity) 1401 | { 1402 | _connector->Execute ( 1403 | "drop table " + 1404 | BOT_ORM_Impl::InjectionHelper::TableName (entity) + 1405 | ";"); 1406 | } 1407 | 1408 | template 1409 | std::enable_if_t::value> 1410 | Insert (const C &, bool = true) 1411 | {} 1412 | template 1413 | std::enable_if_t::value> 1414 | Insert (const C &entity, bool withId = true) 1415 | { 1416 | std::ostringstream os; 1417 | _GetInsert (os, entity, withId); 1418 | _connector->Execute (os.str ()); 1419 | } 1420 | 1421 | template 1422 | std::enable_if_t::value> 1423 | InsertRange (const In &, bool = true) 1424 | {} 1425 | template 1426 | std::enable_if_t::value> 1427 | InsertRange (const In &entities, bool withId = true) 1428 | { 1429 | std::ostringstream os; 1430 | auto anyEntity = false; 1431 | for (const auto &entity : entities) 1432 | { 1433 | _GetInsert (os, entity, withId); 1434 | anyEntity = true; 1435 | } 1436 | if (anyEntity) 1437 | _connector->Execute (os.str ()); 1438 | } 1439 | 1440 | template 1441 | std::enable_if_t::value> 1442 | Update (const C &) 1443 | {} 1444 | template 1445 | std::enable_if_t::value> 1446 | Update (const C &entity) 1447 | { 1448 | std::ostringstream os; 1449 | if (_GetUpdate (os, entity)) 1450 | _connector->Execute (os.str ()); 1451 | } 1452 | 1453 | template 1454 | std::enable_if_t::value> 1455 | UpdateRange (const In &) 1456 | {} 1457 | template 1458 | std::enable_if_t::value> 1459 | UpdateRange (const In &entities) 1460 | { 1461 | std::ostringstream os, osTmp; 1462 | for (const auto &entity : entities) 1463 | if (_GetUpdate (osTmp, entity)) 1464 | { 1465 | os << osTmp.str (); 1466 | osTmp.str (std::string {}); // Flush the previous data 1467 | } 1468 | auto sql = os.str (); 1469 | if (!sql.empty ()) _connector->Execute (sql); 1470 | } 1471 | 1472 | template 1473 | std::enable_if_t::value> 1474 | Update (const C &, 1475 | const Expression::SetExpr &, 1476 | const Expression::Expr &) 1477 | {} 1478 | template 1479 | std::enable_if_t::value> 1480 | Update (const C &entity, 1481 | const Expression::SetExpr &setExpr, 1482 | const Expression::Expr &whereExpr) 1483 | { 1484 | _connector->Execute ( 1485 | "update " + 1486 | BOT_ORM_Impl::InjectionHelper::TableName (entity) + 1487 | " set " + setExpr.ToString () + 1488 | " where " + 1489 | whereExpr.ToString () + ";"); 1490 | } 1491 | 1492 | template 1493 | std::enable_if_t::value> 1494 | Delete (const C &) 1495 | {} 1496 | template 1497 | std::enable_if_t::value> 1498 | Delete (const C &entity) 1499 | { 1500 | const auto &fieldNames = 1501 | BOT_ORM_Impl::InjectionHelper::FieldNames (entity); 1502 | 1503 | std::ostringstream os; 1504 | os << "delete from " 1505 | << BOT_ORM_Impl::InjectionHelper::TableName (entity) 1506 | << " where " << fieldNames[0] << "="; 1507 | 1508 | // Primary Key 1509 | BOT_ORM_Impl::InjectionHelper::Visit ( 1510 | entity, [&os] (const auto &primaryKey, 1511 | const auto & ... dummy) 1512 | { 1513 | // Why 'eatdummy'? 1514 | // Walkaround 'fatal error c1001: 1515 | // an internal error has occurred in the compiler.' on MSVC 14 1516 | auto eatdummy = [] (const auto &) {}; 1517 | (void) eatdummy; 1518 | 1519 | // Why 'dummy'? 1520 | // Walkaround 'template argument deduction/substitution failed' 1521 | // on gcc 5.4 1522 | (void) BOT_ORM_Impl::Expander 1523 | { 1524 | 0, (eatdummy (dummy), 0)... 1525 | }; 1526 | 1527 | if (!BOT_ORM_Impl::SerializationHelper:: 1528 | Serialize (os, primaryKey)) 1529 | os << "null"; 1530 | }); 1531 | os << ";"; 1532 | 1533 | _connector->Execute (os.str ()); 1534 | } 1535 | 1536 | template 1537 | std::enable_if_t::value> 1538 | Delete (const C &, 1539 | const Expression::Expr &) 1540 | {} 1541 | template 1542 | std::enable_if_t::value> 1543 | Delete (const C &entity, 1544 | const Expression::Expr &whereExpr) 1545 | { 1546 | _connector->Execute ( 1547 | "delete from " + 1548 | BOT_ORM_Impl::InjectionHelper::TableName (entity) + 1549 | " where " + 1550 | whereExpr.ToString () + ";"); 1551 | } 1552 | 1553 | template 1554 | std::enable_if_t::value, Queryable> 1555 | Query (C) 1556 | {} 1557 | template 1558 | std::enable_if_t::value, Queryable> 1559 | Query (C queryHelper) 1560 | { 1561 | return Queryable ( 1562 | _connector, 1563 | std::move (queryHelper), 1564 | std::string (" from ") + 1565 | BOT_ORM_Impl::InjectionHelper::TableName (queryHelper)); 1566 | } 1567 | 1568 | protected: 1569 | std::shared_ptr _connector; 1570 | 1571 | static void _GetConstraints ( 1572 | std::string &, 1573 | std::unordered_map &) 1574 | {} 1575 | 1576 | template 1577 | static void _GetConstraints ( 1578 | std::string &tableFixes, 1579 | std::unordered_map &fieldFixes, 1580 | const Constraint &constraint, 1581 | const Args & ... args) 1582 | { 1583 | if (!constraint.field.empty ()) 1584 | fieldFixes[constraint.field] += constraint.constraint; 1585 | else 1586 | tableFixes += constraint.constraint + ","; 1587 | _GetConstraints (tableFixes, fieldFixes, args...); 1588 | } 1589 | 1590 | template 1591 | static inline void _GetInsert ( 1592 | std::ostream &os, const C &entity, bool withId) 1593 | { 1594 | BOT_ORM_Impl::InjectionHelper::Visit ( 1595 | entity, [&os, &entity, withId] ( 1596 | const auto &primaryKey, const auto & ... args) 1597 | { 1598 | const auto &fieldNames = 1599 | BOT_ORM_Impl::InjectionHelper::FieldNames (entity); 1600 | 1601 | os << "insert into " 1602 | << BOT_ORM_Impl::InjectionHelper::TableName (entity) 1603 | << "("; 1604 | 1605 | std::ostringstream osVal; 1606 | 1607 | // Avoid Bad Eating of , 1608 | bool anyField = false; 1609 | 1610 | auto serializeField = 1611 | [&fieldNames, &os, &osVal, &anyField] ( 1612 | const auto &val, size_t index) 1613 | { 1614 | if (BOT_ORM_Impl::SerializationHelper:: 1615 | Serialize (osVal, val)) 1616 | { 1617 | os << fieldNames[index] << ","; 1618 | osVal << ","; 1619 | anyField = true; 1620 | } 1621 | }; 1622 | 1623 | // Priamry Key 1624 | if (withId && 1625 | BOT_ORM_Impl::SerializationHelper:: 1626 | Serialize (osVal, primaryKey)) 1627 | { 1628 | os << fieldNames[0] << ","; 1629 | osVal << ","; 1630 | anyField = true; 1631 | } 1632 | 1633 | // The Rest 1634 | size_t index = 1; 1635 | 1636 | (void) BOT_ORM_Impl::Expander 1637 | { 1638 | 0, (serializeField (args, index++), 0)... 1639 | }; 1640 | (void) serializeField; 1641 | 1642 | if (anyField) 1643 | { 1644 | os.seekp (os.tellp () - std::streamoff (1)); 1645 | osVal.seekp (osVal.tellp () - std::streamoff (1)); 1646 | } 1647 | else // Fix for No Field for Insert... 1648 | { 1649 | os << fieldNames[0]; 1650 | osVal << "null"; 1651 | } 1652 | 1653 | osVal << ");"; // To Enable Seekp... 1654 | os << ") values (" << osVal.str (); 1655 | }); 1656 | } 1657 | 1658 | template 1659 | static inline bool _GetUpdate ( 1660 | std::ostream &os, const C &entity) 1661 | { 1662 | return BOT_ORM_Impl::InjectionHelper::Visit ( 1663 | entity, [&os, &entity] ( 1664 | const auto &primaryKey, const auto & ... args) 1665 | { 1666 | if (sizeof... (args) == 0) 1667 | return false; 1668 | 1669 | const auto &fieldNames = 1670 | BOT_ORM_Impl::InjectionHelper::FieldNames (entity); 1671 | os << "update " 1672 | << BOT_ORM_Impl::InjectionHelper::TableName (entity) 1673 | << " set "; 1674 | 1675 | auto serializeField = [&fieldNames, &os] ( 1676 | const auto &val, size_t index) 1677 | { 1678 | os << fieldNames[index] << "="; 1679 | if (!BOT_ORM_Impl::SerializationHelper:: 1680 | Serialize (os, val)) 1681 | os << "null"; 1682 | os << ","; 1683 | }; 1684 | 1685 | // The Rest 1686 | size_t index = 1; 1687 | 1688 | (void) BOT_ORM_Impl::Expander 1689 | { 1690 | 0, (serializeField (args, index++), 0)... 1691 | }; 1692 | (void) serializeField; 1693 | 1694 | os.seekp (os.tellp () - std::streamoff (1)); 1695 | 1696 | // Primary Key 1697 | os << " where " << fieldNames[0] << "="; 1698 | if (!BOT_ORM_Impl::SerializationHelper:: 1699 | Serialize (os, primaryKey)) 1700 | os << "null"; 1701 | 1702 | os << ";"; 1703 | return true; 1704 | }); 1705 | } 1706 | }; 1707 | 1708 | // Field Extractor 1709 | 1710 | class FieldExtractor 1711 | { 1712 | using pair_type = std::pair< 1713 | const std::string &, 1714 | const std::string &>; 1715 | 1716 | template 1717 | using HasInjected = 1718 | BOT_ORM_Impl::InjectionHelper::HasInjected; 1719 | 1720 | template 1721 | std::enable_if_t::value> 1722 | Extract (const C &) 1723 | {} 1724 | template 1725 | std::enable_if_t::value> 1726 | Extract (const C &helper) 1727 | { 1728 | BOT_ORM_Impl::InjectionHelper::Visit ( 1729 | helper, [this, &helper] ( 1730 | const auto & ... args) 1731 | { 1732 | const auto &fieldNames = 1733 | BOT_ORM_Impl::InjectionHelper::FieldNames (helper); 1734 | const auto &tableName = 1735 | BOT_ORM_Impl::InjectionHelper::TableName (helper); 1736 | 1737 | size_t index = 0; 1738 | (void) BOT_ORM_Impl::Expander 1739 | { 1740 | 0, (_map.emplace ( 1741 | (const void *) &args, 1742 | pair_type { fieldNames[index++], tableName } 1743 | ), 0)... 1744 | }; 1745 | }); 1746 | } 1747 | 1748 | public: 1749 | template 1750 | FieldExtractor (const Classes & ... args) 1751 | { 1752 | (void) BOT_ORM_Impl::Expander 1753 | { 1754 | 0, (Extract (args), 0)... 1755 | }; 1756 | } 1757 | 1758 | template 1759 | inline Expression::Field operator () ( 1760 | const T &field) const 1761 | { 1762 | const auto &result = Get (field); 1763 | return Expression::Field { 1764 | std::move (result.first), &result.second }; 1765 | } 1766 | 1767 | template 1768 | inline Expression::NullableField operator () ( 1769 | const Nullable &field) const 1770 | { 1771 | const auto &result = Get (field); 1772 | return Expression::NullableField { 1773 | std::move (result.first), &result.second }; 1774 | } 1775 | 1776 | private: 1777 | std::unordered_map _map; 1778 | 1779 | template 1780 | const pair_type &Get (const T &field) const 1781 | { 1782 | try { return _map.at ((const void *) &field); } 1783 | catch (...) { throw std::runtime_error (NO_FIELD); } 1784 | } 1785 | }; 1786 | } 1787 | 1788 | // Clear Intra Macros 1789 | #undef NO_ORMAP 1790 | #undef BAD_TYPE 1791 | 1792 | #undef NO_FIELD 1793 | #undef BAD_COLUMN_COUNT 1794 | #undef NULL_DESERIALIZE 1795 | #undef NOT_SAME_TABLE 1796 | 1797 | #endif // !BOT_ORM_H 1798 | -------------------------------------------------------------------------------- /src/sqlite3.c.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOT-Man-JL/ORM-Lite/dd8bd2371f81b13ed6b115084b588149bdf67026/src/sqlite3.c.zip -------------------------------------------------------------------------------- /src/sqlite3.h.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOT-Man-JL/ORM-Lite/dd8bd2371f81b13ed6b115084b588149bdf67026/src/sqlite3.h.zip -------------------------------------------------------------------------------- /test/catch.hpp.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOT-Man-JL/ORM-Lite/dd8bd2371f81b13ed6b115084b588149bdf67026/test/catch.hpp.zip -------------------------------------------------------------------------------- /test/makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET = test 3 | SOURCES = test.cpp ../src/sqlite3.c 4 | WARNINGFLAGS = -Wall -W 5 | CPPFLAGS = -std=c++14 6 | LINKS = -lstdc++ -lpthread -ldl 7 | 8 | OBJS = $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES))) 9 | 10 | %.o: %.c 11 | $(CC) -c $< -o $@ $(WARNINGFLAGS) $(LINKS) 12 | 13 | %.o: %.cpp 14 | $(CC) -c $< -o $@ $(WARNINGFLAGS) $(LINKS) $(CPPFLAGS) 15 | 16 | $(TARGET): $(OBJS) 17 | $(CC) $(OBJS) -o $(TARGET) $(LINKS) 18 | 19 | clean: 20 | rm -rf $(OBJS) $(TARGET) *.db 21 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 |  2 | // Test of ORM Lite 3 | // https://github.com/BOT-Man-JL/ORM-Lite 4 | // BOT Man, 2016 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../src/ormlite.h" 11 | using namespace BOT_ORM; 12 | using namespace BOT_ORM::Expression; 13 | 14 | #define CATCH_CONFIG_MAIN 15 | #include "catch.hpp" 16 | 17 | #define TESTDB "test.db" 18 | 19 | struct ModelA 20 | { 21 | int a_int; 22 | std::string a_string; 23 | double a_double; 24 | 25 | Nullable an_int; 26 | Nullable an_double; 27 | Nullable an_string; 28 | 29 | // Inject ORM-Lite into this Class :-) 30 | ORMAP ("ModelA", a_int, a_string, a_double, an_int, an_double, an_string); 31 | }; 32 | 33 | struct ModelB 34 | { 35 | unsigned long b_ulong; 36 | float b_float; 37 | 38 | Nullable bn_ulong; 39 | Nullable bn_float; 40 | 41 | // Inject ORM-Lite into this Class :-) 42 | ORMAP ("ModelB", b_ulong, b_float, bn_ulong, bn_float); 43 | }; 44 | 45 | struct ModelC 46 | { 47 | unsigned c_uint; 48 | int a_int; 49 | unsigned long b_ulong; 50 | 51 | // Inject ORM-Lite into this Class :-) 52 | ORMAP ("ModelC", c_uint, a_int, b_ulong); 53 | }; 54 | 55 | struct ModelD 56 | { 57 | int d_int; 58 | 59 | // Inject ORM-Lite into this Class :-) 60 | ORMAP ("ModelD", d_int); 61 | }; 62 | 63 | namespace detail 64 | { 65 | template 66 | void ResetTable (const Model &model) 67 | { 68 | ORMapper mapper (TESTDB); 69 | 70 | try { 71 | mapper.CreateTbl (model); 72 | } 73 | catch (...) { 74 | mapper.DropTbl (model); 75 | mapper.CreateTbl (model); 76 | } 77 | } 78 | } 79 | 80 | void ResetTables () {} 81 | 82 | template 83 | void ResetTables (const Model &model, const OtherModels &...models) 84 | { 85 | detail::ResetTable (model); 86 | ResetTables (models...); 87 | } 88 | 89 | TEST_CASE ("create/drop tables") 90 | { 91 | ResetTables (ModelA {}, ModelB {}, ModelC {}, ModelD {}); 92 | 93 | ORMapper mapper (TESTDB); 94 | 95 | mapper.DropTbl (ModelA {}); 96 | mapper.DropTbl (ModelB {}); 97 | mapper.DropTbl (ModelC {}); 98 | mapper.DropTbl (ModelD {}); 99 | 100 | mapper.CreateTbl (ModelA {}); 101 | mapper.CreateTbl (ModelB {}); 102 | mapper.CreateTbl (ModelC {}); 103 | mapper.CreateTbl (ModelD {}); 104 | } 105 | 106 | TEST_CASE ("normal cases") 107 | { 108 | ModelA ma; 109 | ModelD md; 110 | auto field = FieldExtractor { ma, md }; 111 | 112 | ORMapper mapper (TESTDB); 113 | 114 | mapper.Insert (ModelD { 0 }); 115 | mapper.Insert (ModelD { 0 }, false); 116 | mapper.InsertRange (std::list { ModelD { 2 }, ModelD { 3 } }); 117 | mapper.InsertRange (std::list { ModelD { 2 }, ModelD { 3 } }, false); 118 | mapper.Update (ModelD { 0 }); 119 | mapper.UpdateRange (std::list { ModelD { 2 }, ModelD { 3 } }); 120 | mapper.Update (ModelD {}, field (md.d_int) = 6, field (md.d_int) == 0); // 0 -> 6 121 | mapper.Delete (ModelD { 1 }); 122 | mapper.Delete (ModelD {}, field (md.d_int) == 0); // No such one 123 | 124 | constexpr auto countExpected = 5; 125 | constexpr auto firstIdExpected = 2; 126 | constexpr auto lastIdExpected = 6; 127 | // Expected: 2, 3, 4, 5, 6 128 | 129 | REQUIRE (mapper.Query (ModelD {}) 130 | .Aggregate (Count ()).Value () == countExpected); 131 | REQUIRE (mapper.Query (ModelD {}) 132 | .LeftJoin (ModelA {}, field (ma.a_int) == field (md.d_int)) 133 | .Aggregate (Count ()).Value () == countExpected); 134 | 135 | mapper.Insert (ModelA {}, false); 136 | REQUIRE (mapper.Query (ModelD {}).Select (field (md.d_int)) 137 | .Union (mapper.Query (ModelA {}).Select (field (ma.a_int))) 138 | .ToList ().size () == countExpected + 1); 139 | REQUIRE (mapper.Query (ModelD {}) 140 | .ToVector ()[countExpected - 1].d_int == lastIdExpected); 141 | auto firstTuple = mapper.Query (ModelD {}) 142 | .Select (field (md.d_int)) 143 | .ToList ().front (); 144 | REQUIRE (std::get<0> (firstTuple).Value () == firstIdExpected); 145 | } 146 | 147 | TEST_CASE ("handle existing table") 148 | { 149 | // before 150 | ResetTables (ModelD {}); 151 | { 152 | sqlite3 *db; 153 | sqlite3_open (TESTDB, &db); 154 | sqlite3_exec (db, 155 | "DROP TABLE ModelD;" 156 | "CREATE TABLE ModelD (d_int INTEGER, d_str TEXT);" 157 | "INSERT INTO ModelD values (1, 'John');", 158 | nullptr, nullptr, nullptr); 159 | sqlite3_close (db); 160 | } 161 | 162 | // test 163 | ORMapper mapper (TESTDB); 164 | REQUIRE_THROWS_WITH (mapper.Query (ModelD {}).ToList (), 165 | "SQL error: 'Bad Column Count' at 'select * from ModelD;'"); 166 | } 167 | 168 | TEST_CASE ("chinese characters") 169 | { 170 | // before 171 | ResetTables (ModelA {}); 172 | 173 | // test 174 | ORMapper mapper (TESTDB); 175 | 176 | mapper.Insert ( 177 | ModelA { 0, "你好", 0, nullptr, nullptr, nullptr }, false); 178 | mapper.Insert ( 179 | ModelA { 0, u8"世界", 0, nullptr, nullptr, nullptr }, false); 180 | 181 | auto chinese = mapper.Query (ModelA {}).ToVector (); 182 | 183 | REQUIRE (chinese.size () == 2); 184 | REQUIRE (chinese[0].a_string == "你好"); 185 | REQUIRE (chinese[1].a_string == u8"世界"); 186 | } 187 | 188 | TEST_CASE ("lifetime of mapper") 189 | { 190 | // before 191 | ResetTables (ModelA {}); 192 | { 193 | ORMapper mapper (TESTDB); 194 | mapper.Insert (ModelA {}, false); 195 | mapper.Insert (ModelA {}, false); 196 | } 197 | 198 | // test 199 | std::unique_ptr> queryable; 200 | { 201 | ORMapper mapper (TESTDB); 202 | queryable.reset (new Queryable { mapper.Query (ModelA {}) }); 203 | } 204 | REQUIRE (queryable->ToList ().size () == 2); 205 | } 206 | 207 | using InvalidModel = int; 208 | 209 | TEST_CASE ("invalid model", "[not-compile]") 210 | { 211 | ORMapper mapper (TESTDB); 212 | 213 | //mapper.CreateTbl (InvalidModel {}); 214 | //mapper.CreateTbl (InvalidModel {}, Constraint::Unique (Field {"", nullptr})); 215 | //mapper.DropTbl (InvalidModel {}); 216 | 217 | //mapper.Insert (InvalidModel {}); 218 | //mapper.Insert (InvalidModel {}, false); 219 | ////mapper.InsertRange (InvalidModel {}); 220 | //mapper.InsertRange (std::vector {}); 221 | //mapper.InsertRange (std::vector {}, false); 222 | //mapper.Update (InvalidModel {}); 223 | ////mapper.UpdateRange (InvalidModel {}); 224 | //mapper.UpdateRange (std::vector {}); 225 | //mapper.Update (InvalidModel {}, SetExpr { "" }, Expr { Selectable {"", nullptr}, "" }); 226 | //mapper.Delete (InvalidModel {}); 227 | //mapper.Delete (InvalidModel {}, Expr { Selectable {"", nullptr}, "" }); 228 | 229 | //mapper.Query (InvalidModel {}); 230 | //FieldExtractor { InvalidModel {}, double () }; 231 | ////mapper.Query (ModelA {}) 232 | //// .Join (InvalidModel {}, Expr { Selectable {"", nullptr}, "" }); 233 | ////mapper.Query (ModelA {}) 234 | //// .LeftJoin (InvalidModel {}, Expr { Selectable {"", nullptr}, "" }); 235 | } 236 | 237 | struct SickModel 238 | { 239 | ModelD dd; 240 | char ch; 241 | ORMAP ("SickModel", dd, ch); 242 | }; 243 | 244 | TEST_CASE ("invalid field", "[not-compile]") 245 | { 246 | ORMapper mapper (TESTDB); 247 | 248 | //mapper.CreateTbl (SickModel {}); 249 | //mapper.Insert (SickModel {}); 250 | //mapper.Update (SickModel {}); 251 | //mapper.Delete (SickModel {}); 252 | //mapper.Query (SickModel {}).ToList (); 253 | } 254 | -------------------------------------------------------------------------------- /test/test.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1781A74F1F5FED13008E6067 /* test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1781A74E1F5FED13008E6067 /* test.cpp */; }; 11 | 1781A7531F5FEDB3008E6067 /* sqlite3.c in Sources */ = {isa = PBXBuildFile; fileRef = 1781A7511F5FEDB3008E6067 /* sqlite3.c */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | 1781A7421F5FECD1008E6067 /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = /usr/share/man/man1/; 19 | dstSubfolderSpec = 0; 20 | files = ( 21 | ); 22 | runOnlyForDeploymentPostprocessing = 1; 23 | }; 24 | /* End PBXCopyFilesBuildPhase section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 1781A7441F5FECD1008E6067 /* test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = test; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 1781A74E1F5FED13008E6067 /* test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test.cpp; sourceTree = ""; }; 29 | 1781A7541F5FEE5D008E6067 /* nullable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nullable.h; path = ../src/nullable.h; sourceTree = ""; }; 30 | 1781A7501F5FED8A008E6067 /* ormlite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ormlite.h; path = ../src/ormlite.h; sourceTree = ""; }; 31 | 1781A7511F5FEDB3008E6067 /* sqlite3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sqlite3.c; path = ../src/sqlite3.c; sourceTree = ""; }; 32 | 1781A7521F5FEDB3008E6067 /* sqlite3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sqlite3.h; path = ../src/sqlite3.h; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | 1781A7411F5FECD1008E6067 /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | 1781A73B1F5FECD1008E6067 = { 47 | isa = PBXGroup; 48 | children = ( 49 | 1781A7541F5FEE5D008E6067 /* nullable.h */, 50 | 1781A7501F5FED8A008E6067 /* ormlite.h */, 51 | 1781A7511F5FEDB3008E6067 /* sqlite3.c */, 52 | 1781A7521F5FEDB3008E6067 /* sqlite3.h */, 53 | 1781A74E1F5FED13008E6067 /* test.cpp */, 54 | 1781A7451F5FECD1008E6067 /* Products */, 55 | ); 56 | sourceTree = ""; 57 | }; 58 | 1781A7451F5FECD1008E6067 /* Products */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 1781A7441F5FECD1008E6067 /* test */, 62 | ); 63 | name = Products; 64 | sourceTree = ""; 65 | }; 66 | /* End PBXGroup section */ 67 | 68 | /* Begin PBXNativeTarget section */ 69 | 1781A7431F5FECD1008E6067 /* test */ = { 70 | isa = PBXNativeTarget; 71 | buildConfigurationList = 1781A74B1F5FECD1008E6067 /* Build configuration list for PBXNativeTarget "test" */; 72 | buildPhases = ( 73 | 1781A7401F5FECD1008E6067 /* Sources */, 74 | 1781A7411F5FECD1008E6067 /* Frameworks */, 75 | 1781A7421F5FECD1008E6067 /* CopyFiles */, 76 | ); 77 | buildRules = ( 78 | ); 79 | dependencies = ( 80 | ); 81 | name = test; 82 | productName = test; 83 | productReference = 1781A7441F5FECD1008E6067 /* test */; 84 | productType = "com.apple.product-type.tool"; 85 | }; 86 | /* End PBXNativeTarget section */ 87 | 88 | /* Begin PBXProject section */ 89 | 1781A73C1F5FECD1008E6067 /* Project object */ = { 90 | isa = PBXProject; 91 | attributes = { 92 | LastUpgradeCheck = 0830; 93 | TargetAttributes = { 94 | 1781A7431F5FECD1008E6067 = { 95 | CreatedOnToolsVersion = 8.3.3; 96 | ProvisioningStyle = Automatic; 97 | }; 98 | }; 99 | }; 100 | buildConfigurationList = 1781A73F1F5FECD1008E6067 /* Build configuration list for PBXProject "test" */; 101 | compatibilityVersion = "Xcode 3.2"; 102 | developmentRegion = English; 103 | hasScannedForEncodings = 0; 104 | knownRegions = ( 105 | en, 106 | ); 107 | mainGroup = 1781A73B1F5FECD1008E6067; 108 | productRefGroup = 1781A7451F5FECD1008E6067 /* Products */; 109 | projectDirPath = ""; 110 | projectRoot = ""; 111 | targets = ( 112 | 1781A7431F5FECD1008E6067 /* test */, 113 | ); 114 | }; 115 | /* End PBXProject section */ 116 | 117 | /* Begin PBXSourcesBuildPhase section */ 118 | 1781A7401F5FECD1008E6067 /* Sources */ = { 119 | isa = PBXSourcesBuildPhase; 120 | buildActionMask = 2147483647; 121 | files = ( 122 | 1781A74F1F5FED13008E6067 /* test.cpp in Sources */, 123 | 1781A7531F5FEDB3008E6067 /* sqlite3.c in Sources */, 124 | ); 125 | runOnlyForDeploymentPostprocessing = 0; 126 | }; 127 | /* End PBXSourcesBuildPhase section */ 128 | 129 | /* Begin XCBuildConfiguration section */ 130 | 1781A7491F5FECD1008E6067 /* Debug */ = { 131 | isa = XCBuildConfiguration; 132 | buildSettings = { 133 | ALWAYS_SEARCH_USER_PATHS = NO; 134 | CLANG_ANALYZER_NONNULL = YES; 135 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 136 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 137 | CLANG_CXX_LIBRARY = "libc++"; 138 | CLANG_ENABLE_MODULES = YES; 139 | CLANG_ENABLE_OBJC_ARC = YES; 140 | CLANG_WARN_BOOL_CONVERSION = YES; 141 | CLANG_WARN_CONSTANT_CONVERSION = YES; 142 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 143 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 144 | CLANG_WARN_EMPTY_BODY = YES; 145 | CLANG_WARN_ENUM_CONVERSION = YES; 146 | CLANG_WARN_INFINITE_RECURSION = YES; 147 | CLANG_WARN_INT_CONVERSION = YES; 148 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 149 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 150 | CLANG_WARN_UNREACHABLE_CODE = YES; 151 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 152 | CODE_SIGN_IDENTITY = "-"; 153 | COPY_PHASE_STRIP = NO; 154 | DEBUG_INFORMATION_FORMAT = dwarf; 155 | ENABLE_STRICT_OBJC_MSGSEND = YES; 156 | ENABLE_TESTABILITY = YES; 157 | GCC_C_LANGUAGE_STANDARD = gnu99; 158 | GCC_DYNAMIC_NO_PIC = NO; 159 | GCC_NO_COMMON_BLOCKS = YES; 160 | GCC_OPTIMIZATION_LEVEL = 0; 161 | GCC_PREPROCESSOR_DEFINITIONS = ( 162 | "DEBUG=1", 163 | "$(inherited)", 164 | ); 165 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 166 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 167 | GCC_WARN_UNDECLARED_SELECTOR = YES; 168 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 169 | GCC_WARN_UNUSED_FUNCTION = YES; 170 | GCC_WARN_UNUSED_VARIABLE = YES; 171 | MACOSX_DEPLOYMENT_TARGET = 10.12; 172 | MTL_ENABLE_DEBUG_INFO = YES; 173 | ONLY_ACTIVE_ARCH = YES; 174 | SDKROOT = macosx; 175 | }; 176 | name = Debug; 177 | }; 178 | 1781A74A1F5FECD1008E6067 /* Release */ = { 179 | isa = XCBuildConfiguration; 180 | buildSettings = { 181 | ALWAYS_SEARCH_USER_PATHS = NO; 182 | CLANG_ANALYZER_NONNULL = YES; 183 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 184 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 185 | CLANG_CXX_LIBRARY = "libc++"; 186 | CLANG_ENABLE_MODULES = YES; 187 | CLANG_ENABLE_OBJC_ARC = YES; 188 | CLANG_WARN_BOOL_CONVERSION = YES; 189 | CLANG_WARN_CONSTANT_CONVERSION = YES; 190 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 191 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 192 | CLANG_WARN_EMPTY_BODY = YES; 193 | CLANG_WARN_ENUM_CONVERSION = YES; 194 | CLANG_WARN_INFINITE_RECURSION = YES; 195 | CLANG_WARN_INT_CONVERSION = YES; 196 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 197 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 198 | CLANG_WARN_UNREACHABLE_CODE = YES; 199 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 200 | CODE_SIGN_IDENTITY = "-"; 201 | COPY_PHASE_STRIP = NO; 202 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 203 | ENABLE_NS_ASSERTIONS = NO; 204 | ENABLE_STRICT_OBJC_MSGSEND = YES; 205 | GCC_C_LANGUAGE_STANDARD = gnu99; 206 | GCC_NO_COMMON_BLOCKS = YES; 207 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 208 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 209 | GCC_WARN_UNDECLARED_SELECTOR = YES; 210 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 211 | GCC_WARN_UNUSED_FUNCTION = YES; 212 | GCC_WARN_UNUSED_VARIABLE = YES; 213 | MACOSX_DEPLOYMENT_TARGET = 10.12; 214 | MTL_ENABLE_DEBUG_INFO = NO; 215 | SDKROOT = macosx; 216 | }; 217 | name = Release; 218 | }; 219 | 1781A74C1F5FECD1008E6067 /* Debug */ = { 220 | isa = XCBuildConfiguration; 221 | buildSettings = { 222 | CLANG_CXX_LANGUAGE_STANDARD = "c++14"; 223 | PRODUCT_NAME = "$(TARGET_NAME)"; 224 | }; 225 | name = Debug; 226 | }; 227 | 1781A74D1F5FECD1008E6067 /* Release */ = { 228 | isa = XCBuildConfiguration; 229 | buildSettings = { 230 | CLANG_CXX_LANGUAGE_STANDARD = "c++14"; 231 | PRODUCT_NAME = "$(TARGET_NAME)"; 232 | }; 233 | name = Release; 234 | }; 235 | /* End XCBuildConfiguration section */ 236 | 237 | /* Begin XCConfigurationList section */ 238 | 1781A73F1F5FECD1008E6067 /* Build configuration list for PBXProject "test" */ = { 239 | isa = XCConfigurationList; 240 | buildConfigurations = ( 241 | 1781A7491F5FECD1008E6067 /* Debug */, 242 | 1781A74A1F5FECD1008E6067 /* Release */, 243 | ); 244 | defaultConfigurationIsVisible = 0; 245 | defaultConfigurationName = Release; 246 | }; 247 | 1781A74B1F5FECD1008E6067 /* Build configuration list for PBXNativeTarget "test" */ = { 248 | isa = XCConfigurationList; 249 | buildConfigurations = ( 250 | 1781A74C1F5FECD1008E6067 /* Debug */, 251 | 1781A74D1F5FECD1008E6067 /* Release */, 252 | ); 253 | defaultConfigurationIsVisible = 0; 254 | defaultConfigurationName = Release; 255 | }; 256 | /* End XCConfigurationList section */ 257 | }; 258 | rootObject = 1781A73C1F5FECD1008E6067 /* Project object */; 259 | } 260 | -------------------------------------------------------------------------------- /test/test.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {AD41C840-0ADD-43E1-BF17-60EC79CEEEFC} 23 | Win32Proj 24 | test 25 | 26 | 27 | 28 | Application 29 | true 30 | v141 31 | Unicode 32 | 33 | 34 | Application 35 | false 36 | v141 37 | true 38 | Unicode 39 | 40 | 41 | Application 42 | true 43 | v141 44 | Unicode 45 | 46 | 47 | Application 48 | false 49 | v141 50 | true 51 | Unicode 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | true 73 | $(SolutionDir)$(Configuration)\test\ 74 | $(Configuration)\test\ 75 | 76 | 77 | true 78 | $(Platform)\$(Configuration)\test\ 79 | $(SolutionDir)$(Platform)\$(Configuration)\test\ 80 | 81 | 82 | false 83 | 84 | 85 | false 86 | 87 | 88 | 89 | 90 | 91 | Level3 92 | Disabled 93 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 94 | 95 | 96 | Console 97 | true 98 | 99 | 100 | 101 | 102 | 103 | 104 | Level3 105 | Disabled 106 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 107 | 108 | 109 | Console 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | 117 | 118 | MaxSpeed 119 | true 120 | true 121 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | 123 | 124 | Console 125 | true 126 | true 127 | true 128 | 129 | 130 | 131 | 132 | Level3 133 | 134 | 135 | MaxSpeed 136 | true 137 | true 138 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 139 | 140 | 141 | Console 142 | true 143 | true 144 | true 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | --------------------------------------------------------------------------------