Repository: BOT-Man-JL/ORM-Lite
Branch: master
Commit: dd8bd2371f81
Files: 17
Total size: 149.4 KB
Directory structure:
gitextract_a6u7d0as/
├── .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
└── test/
├── makefile
├── test.cpp
├── test.pbxproj
└── test.vcxproj
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
[Xx]64/
[Xx]86/
[Bb]uild/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Un-comment the next line if you do not want to checkin
# your web deploy settings because they may include unencrypted
# passwords
#*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directory
AppPackages/
BundleArtifacts/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# LightSwitch generated files
GeneratedArtifacts/
ModelManifest.xml
# Paket dependency manager
.paket/paket.exe
# FAKE - F# Make
.fake/
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
# SQLite
src/sqlite3.c
src/sqlite3.h
# Catch
catch.hpp
# Make Output
*.db
*.o
sample/sample
test/test
================================================
FILE: .travis.yml
================================================
language: cpp
sudo: required
compiler:
- gcc
- clang
script:
- unzip -qu src/sqlite3.h.zip -d src
- unzip -qu src/sqlite3.c.zip -d src
- unzip -qu test/catch.hpp.zip -d test
- make -C sample && ./sample/sample
- make -C test && ./test/test
install:
- if [ "$CC" = "gcc" ]; then export CXX="g++-5" CC="gcc-5"; fi
- if [ "$CC" = "clang" ]; then export CXX="clang++-3.8" CC="clang-3.8"; fi
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.8
packages:
- gcc-5
- g++-5
- clang-3.8
- unzip
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2016 BOT Man JL
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# ORM Lite
**ORM Lite** is a C++ [_**Object Relation Mapping** (ORM)_](https://en.wikipedia.org/wiki/Object-relational_mapping)
for **SQLite3** (currently 😂),
written in Modern C++ style.
[](https://ci.appveyor.com/project/BOT-Man-JL/ORM-Lite)
[](https://travis-ci.org/BOT-Man-JL/ORM-Lite)
## Features
- **Easy** to Use
- **Header Only**
([src/ormlite.h](src/ormlite.h), [src/nullable.h](src/nullable.h))
- **Powerful** Compile-time **Type/DSL Deduction**
## Documentation
#### [Get Started Here](docs/get-started.md) 😉
#### [Full Document](docs/orm-lite.md) 😊
## Planned Features
- Support More Databases (Looking for a Better Driver recently...)
- Customized Primary Key (Hard to Design an Elegant Interface for it...)
- Blob (Hard to be Serialized to Script...)
- Date/Time Types (Weak Typed in SQL...)
- Subquery (Too Complicated... the Interface would be)
Feel free to [Issue](https://github.com/BOT-Man-JL/ORM-Lite/issues/new),
if you have any idea. 😎
## Implementation Details (实现细节)
Posts in **Chinese** only:
- [How to Design a Naive C++ ORM](https://BOT-Man-JL.github.io/articles/?post=2016/How-to-Design-a-Naive-Cpp-ORM)
- [How to Design a Better C++ ORM](https://bot-man-jl.github.io/articles/?post=2016/How-to-Design-a-Better-Cpp-ORM)
================================================
FILE: appveyor.yml
================================================
version: 1.0.{build}
image: Visual Studio 2017
build_script:
- ps: Expand-Archive src/sqlite3.h.zip -DestinationPath src
- ps: Expand-Archive src/sqlite3.c.zip -DestinationPath src
- ps: Expand-Archive test/catch.hpp.zip -DestinationPath test
- ps: msbuild /verbosity:minimal sample/sample.vcxproj "/p:Configuration=Debug;Platform=Win32"
- ps: msbuild /verbosity:minimal sample/sample.vcxproj "/p:Configuration=Debug;Platform=x64"
- ps: msbuild /verbosity:minimal sample/sample.vcxproj "/p:Configuration=Release;Platform=Win32"
- ps: msbuild /verbosity:minimal sample/sample.vcxproj "/p:Configuration=Release;Platform=x64"
- ps: msbuild /verbosity:minimal test/test.vcxproj "/p:Configuration=Debug;Platform=Win32"
- ps: msbuild /verbosity:minimal test/test.vcxproj "/p:Configuration=Debug;Platform=x64"
- ps: msbuild /verbosity:minimal test/test.vcxproj "/p:Configuration=Release;Platform=Win32"
- ps: msbuild /verbosity:minimal test/test.vcxproj "/p:Configuration=Release;Platform=x64"
test_script:
- ps: sample\Debug\sample\sample.exe
- ps: sample\Release\sample.exe
- ps: sample\x64\Debug\sample\sample.exe
- ps: sample\x64\Release\sample.exe
- ps: test\Debug\test\test.exe
- ps: test\Release\test.exe
- ps: test\x64\Debug\test\test.exe
- ps: test\x64\Release\test.exe
================================================
FILE: docs/get-started.md
================================================
# Get Started
Here is a **short tour** for this **Amazing** ORM 😉
The full code is in [sample.cpp](../sample/sample.cpp).
## Preparation
Before we start, Include **src** into your Project:
- `ORMLite.h`
- **SQLite3** Dependency
> If you haven't installed **SQLite3**,
> you can find the zip file of `sqlite3.h` and `sqlite3.c` in `/src`
## Including *ORM Lite*
``` cpp
#include "ORMLite.h"
using namespace BOT_ORM;
using namespace BOT_ORM::Expression;
struct UserModel
{
int user_id;
std::string user_name;
double credit_count;
Nullable<int> age;
Nullable<double> salary;
Nullable<std::string> title;
// Inject ORM-Lite into this Class :-)
ORMAP ("UserModel", user_id, user_name, credit_count,
age, salary, title);
};
```
`Nullable<T>` helps us construct `Nullable` Value in C++,
which is described in the [Document](orm-lite.md) 😁
In this sample, `ORMAP ("UserModel", ...)` do that:
- `Class UserModel` will be mapped into `TABLE UserModel`;
- NOT `Nullable` members will be mapped as `NOT NULL`;
- `int, double, std::string` will be mapped into
`INT, REAL, TEXT` respectively;
- The first entry `id` will be set as the **Primary Key** of the Table;
And then we define and inject other 2 Classes in this sample:
``` cpp
struct SellerModel
{
int seller_id;
std::string seller_name;
double credit_count;
// Inject ORM-Lite into this Class :-)
ORMAP ("SellerModel", seller_id, seller_name, credit_count);
};
struct OrderModel
{
int order_id;
int user_id;
int seller_id;
std::string product_name;
Nullable<double> fee;
// Inject ORM-Lite into this Class :-)
ORMAP ("OrderModel", order_id, user_id, seller_id,
product_name, fee);
};
```
## Field Extracting
- `FieldExtractor`
``` cpp
// Define more Query Helper Objects and their Field Extractor
UserModel user;
SellerModel seller;
OrderModel order;
auto field = FieldExtractor { user, seller, order };
// Extract Field from 'field'
// For example: field (user.user_name)
// => Retrieve the field of user_name in UserModel table
```
## Working on *Database* with *ORMapper*
- `ORMapper`
``` cpp
// Open a Connection with 'sample.db'
ORMapper mapper ("sample.db");
```
## Create or Drop Tables
- `ORMapper.CreateTbl`
- `ORMapper.DropTbl`
- `Expression`
``` cpp
// Create Table with Constraints
mapper.CreateTbl (
UserModel {},
Constraint::Default (field (user.salary), 1000.0));
// Remarks:
// CREATE TABLE UserModel(
// user_id INTEGER NOT NULL PRIMARY KEY,
// user_name TEXT NOT NULL,
// credit_count REAL NOT NULL,
// age INTEGER,
// salary REAL DEFAULT 1000,
// title TEXT);
mapper.CreateTbl (
SellerModel {},
Constraint::Check (field (seller.credit_count) > 0.0));
// Remarks:
// CREATE TABLE SellerModel(
// seller_id INTEGER NOT NULL PRIMARY KEY,
// seller_name TEXT NOT NULL,
// credit_count REAL NOT NULL,
// CHECK (credit_count > 0));
mapper.CreateTbl (
OrderModel {},
Constraint::Reference (
field (order.user_id), field (user.user_id)),
Constraint::Reference (
field (order.seller_id), field (seller.seller_id)),
Constraint::Unique (Constraint::CompositeField {
field (order.product_name), field (order.fee) }));
// Remarks:
// CREATE TABLE OrderModel(
// order_id INTEGER NOT NULL PRIMARY KEY,
// user_id INTEGER NOT NULL,
// seller_id INTEGER NOT NULL,
// product_name TEXT NOT NULL,
// fee REAL,
// FOREIGN KEY (user_id) REFERENCES UserModel(user_id),
// FOREIGN KEY (seller_id) REFERENCES SellerModel(seller_id),
// UNIQUE (product_name, fee));
...
// Drop Tables
mapper.DropTbl (UserModel {});
mapper.DropTbl (SellerModel {});
mapper.DropTbl (OrderModel {});
```
#### User Model
| user_id| user_name| credit_count| age| salary| title|
|--------|----------|-------------|-------|--------|-------|
| 0| John| 0.2| 21| 1000.0| `null`|
| 1| Jack| 0.4| `null`| 3.14| `null`|
| 2| Jess| 0.6| `null`| 1000.0| Dr.|
| ...| ...| ...| ...| ...| ...|
#### Seller Model
| seller_id| seller_name| credit_count|
|----------|------------|-------------|
| ...| ...| ...|
#### Order Model
| order_id| user_id| seller_id| product_name| fee|
|---------|--------|----------|-------------|----|
| ...| ...| ...| ...| ...|
## Basic CRUD
- `ORMapper.Insert` (Create)
- `ORMapper.Query` (Read)
- `ORMapper.Update` (Update)
- `ORMapper.Delete` (Delete)
- `ORMapper.Transaction`
- `Table Constraints`
``` cpp
std::vector<UserModel> initObjs =
{
{ 0, "John", 0.2, 21, nullptr, nullptr },
{ 1, "Jack", 0.4, nullptr, 3.14, nullptr },
{ 2, "Jess", 0.6, nullptr, nullptr, std::string ("Dr.") }
};
// Insert Values with Primary Key
for (const auto &obj : initObjs)
mapper.Insert (obj);
initObjs[1].salary = nullptr;
initObjs[1].title = "St.";
// Update Entity by Primary Key (WHERE UserModel.id = 1)
mapper.Update (initObjs[1]);
// Delete Entity by Primary Key (WHERE UserModel.id = 2)
mapper.Delete (initObjs[2]);
// Transactional Statements
try
{
mapper.Transaction ([&] ()
{
mapper.Delete (initObjs[0]); // OK
mapper.Insert (UserModel {
1, "Joke", 0, nullptr, nullptr, nullptr
}); // Failed
});
}
catch (const std::exception &ex)
{
// If any statement Failed, throw an exception
std::cout << ex.what () << std::endl;
// SQL error: 'UNIQUE constraint failed: UserModel.id'
// Remarks:
// mapper.Delete (initObjs[0]); will not applied :-)
}
// Select All to List
auto result1 = mapper.Query (UserModel {}).ToList ();
// decltype (result1) == std::list<UserModel>
// result1 = [{ 0, 0.2, "John", 21, 1000, null },
// { 1, 0.4, "Jack", null, null, "St." }]
// Table Constraints
try
{
// Insert Values without Primary Key
mapper.Insert (SellerModel { 0, "John Inc.", 0.0 }, false);
}
catch (const std::exception &ex)
{
std::cout << ex.what () << std::endl;
// SQL error: 'CHECK constraint failed: SellerModel'
}
```
## Batch Operations
- `ORMapper.InsertRange`
- `ORMapper.UpdateRange`
``` cpp
std::vector<UserModel> dataToSeed;
for (int i = 50; i < 100; i++)
dataToSeed.emplace_back (UserModel {
i, "July_" + std::to_string (i), i * 0.2,
nullptr, nullptr, nullptr
});
// Insert by Batch Insert
mapper.Transaction ([&] () {
mapper.InsertRange (dataToSeed);
});
for (size_t i = 0; i < 20; i++)
{
dataToSeed[i + 30].age = 30 + (int) i / 2;
dataToSeed[i + 20].title = "Mr. " + std::to_string (i);
}
// Update by Batch Update
mapper.Transaction ([&] () {
// Note that: it will erase the default value of 'salary'
mapper.UpdateRange (dataToSeed);
});
```
## Single-Table Query
- `Expression`
- `Aggregate`
- `Queryable.Where`
- `Queryable.OrderBy`
- `Queryable.Take`
- `Queryable.Skip`
- `Queryable.Select`
``` cpp
// Select by Condition
auto result2 = mapper.Query (UserModel {})
.Where (
field (user.user_name) & std::string ("July%") &&
(field (user.age) >= 32 &&
field (user.title) != nullptr)
)
.OrderByDescending (field (user.age))
.OrderBy (field (user.user_id))
.Take (3)
.Skip (1)
.ToVector ();
// Remarks:
// SELECT * FROM UserModel
// WHERE (user_name LIKE 'July%' AND
// (age >= 32 AND title IS NOT NULL))
// ORDER BY age DESC, user_id
// LIMIT 3 OFFSET 1
// decltype (result2) == std::vector<UserModel>
// result2 = [{ 89, 17.8, "July_89", 34, null, "Mr. 19" },
// { 86, 17.2, "July_86", 33, null, "Mr. 16" },
// { 87, 17.4, "July_87", 33, null, "Mr. 17" }]
// Calculate Aggregate Function
auto avg = mapper.Query (UserModel {})
.Where (field (user.user_name) & std::string ("July%"))
.Aggregate (Avg (field (user.credit_count)));
// Remarks:
// SELECT AVG (credit_count) FROM UserModel
// WHERE (user_name LIKE 'July%')
// avg = 14.9
auto count = mapper.Query (UserModel {})
.Where (field (user.user_name) | std::string ("July%"))
.Aggregate (Count ());
// Remarks:
// SELECT COUNT (*) FROM UserModel
// WHERE (user_name NOT LIKE 'July%')
// count = 2
```
## Update / Delete by Statement
- `Expression`
- `ORMapper.Update`
- `ORMapper.Delete`
``` cpp
// Update by Condition
mapper.Update (
UserModel {},
(field (user.age) = 10) &&
(field (user.credit_count) = 1.0),
field (user.user_name) == std::string ("July"));
// Remarks:
// UPDATE UserModel SET age = 10,credit_count = 1.0
// WHERE (user_name = 'July')
// Delete by Condition
mapper.Delete (UserModel {},
field (user.user_id) >= 90);
// Remarks:
// DELETE FROM UserModel WHERE (id >= 90)
```
## Multi-Table Query
- `Expression`
- `Aggregate`
- `Queryable.Join`
- `Queryable.LeftJoin`
- `Queryable.Select`
- `Queryable.GroupBy`
- `Queryable.Having`
- `Queryable.Union`
``` cpp
mapper.Transaction ([&] ()
{
for (size_t i = 0; i < 50; i++)
{
mapper.Insert (
SellerModel { (int) i + 50,
"Seller " + std::to_string (i), 3.14 });
mapper.Insert (
OrderModel { 0,
(int) i / 2 + 50,
(int) i / 4 + 50,
"Item " + std::to_string (i),
i * 0.5 }, false);
}
});
// Join Tables for Query
auto joinedQuery = mapper.Query (UserModel {})
.Join (OrderModel {},
field (user.user_id) ==
field (order.user_id))
.LeftJoin (SellerModel {},
field (seller.seller_id) ==
field (order.seller_id))
.Where (field (user.user_id) >= 65);
// Get Result to List
auto result3 = joinedQuery.ToList ();
// Remarks:
// SELECT * FROM UserModel
// JOIN OrderModel
// ON UserModel.user_id=OrderModel.user_id
// LEFT JOIN SellerModel
// ON SellerModel.seller_id=OrderModel.seller_id
// WHERE (UserModel.user_id>=65)
// decltype (result3) == std::list<std::tuple<Nullable<..>, ..>>
// result3 = [(65, "July_65", 13, null, null, null,
// 31, 65, 57, "Item 30", 15,
// 57, "Seller 7", 3.14),
// (65, "July_65", 13, null, null, null,
// 32, 65, 57, "Item 31", 15.5,
// 57, "Seller 7", 3.14),
// ... ]
// Group & Having ~
auto result4 = joinedQuery
.Select (field (order.user_id),
field (user.user_name),
Avg (field (order.fee)))
.GroupBy (field (user.user_name))
.Having (Sum (field (order.fee)) >= 40.5)
.Skip (3)
.ToList ();
// Remarks:
// SELECT OrderModel.user_id,
// UserModel.user_name,
// AVG (OrderModel.fee)
// FROM UserModel
// JOIN OrderModel
// ON UserModel.user_id=OrderModel.user_id
// LEFT JOIN SellerModel
// ON SellerModel.seller_id=OrderModel.seller_id
// WHERE (UserModel.user_id>=65)
// GROUP BY UserModel.user_name
// HAVING SUM (OrderModel.fee)>=40.5
// LIMIT ~0 OFFSET 3
// decltype (result4) == std::list<std::tuple<Nullable<..>, ..>>
// result4 = [(73, "July_73", 23.25),
// (74, "July_74", 24.25)]
// Compound Select
// Results are Nullable-Tuples
auto result5 = mapper.Query (OrderModel {})
.Select (field (order.product_name), field (order.user_id))
.Where (field (order.user_id) == 50)
.Union (
joinedQuery
.Select (field (user.user_name), field (order.order_id))
)
.Take (4)
.ToList ();
// Remarks:
// SELECT OrderModel.product_name,
// OrderModel.user_id
// FROM OrderModel
// WHERE (OrderModel.user_id==50)
// UNION
// SELECT UserModel.user_name,
// OrderModel.order_id
// FROM UserModel
// JOIN OrderModel
// ON UserModel.user_id=OrderModel.user_id
// LEFT JOIN SellerModel
// ON SellerModel.seller_id=OrderModel.seller_id
// WHERE (UserModel.user_id>=65)
// LIMIT 4;
// decltype (result5) == std::list<std::tuple<Nullable<..>, ..>>
// result5 = [("Item 0", 50),
// ("Item 1", 50),
// ("July_65", 31),
// ("July_65", 32)]
```
## Wrapping Up
This little ORM is very cool, enjoy it. 😇
See the **[Full Document Here](orm-lite.md)**.
================================================
FILE: docs/orm-lite.md
================================================
# ORM Lite
## Requirements
- **C++ 14** Support
- MSVC >= 14 (VS 2015 Update 3)
- gcc >= 5.4
- Clang >= 3.8
- **SQLite 3** Dependency
## Mapping
| SQL Concepts | C++ Concepts | Notes |
|---------------------|--------------|-------|
| Table / Relation | Object | Deduced by `ORMAP` |
| Tuple (in `SELECT`) | `std::tuple` | Deduced by `Join` / `Select` |
| Column | Field | Decuced by `Field` |
## `BOT_ORM`
``` cpp
#include "ORMLite.h"
using namespace BOT_ORM;
using namespace BOT_ORM::Expression;
```
Macro `ORMAP` in `ORMLite.h`
- `ORMAP (TableName, PrimaryKey, ...);`
Modules under `namespace BOT_ORM`
- `BOT_ORM::Nullable`
- `BOT_ORM::ORMapper`
- `BOT_ORM::Queryable<QueryResult>`
- `BOT_ORM::FieldExtractor`
- `BOT_ORM::Constraint`
Modules under `namespace BOT_ORM::Expression`
- `BOT_ORM::Expression::Selectable`
- `BOT_ORM::Expression::Field`
- `BOT_ORM::Expression::NullableField`
- `BOT_ORM::Expression::Aggregate`
- `BOT_ORM::Expression::Expr`
- `BOT_ORM::Expression::SetExpr`
- `BOT_ORM::Expression::Count ()`
- `BOT_ORM::Expression::Sum ()`
- `BOT_ORM::Expression::Avg ()`
- `BOT_ORM::Expression::Max ()`
- `BOT_ORM::Expression::Min ()`
Static Modules under `class BOT_ORM::Constraint`
- `BOT_ORM::Constraint::CompositeField`
- `BOT_ORM::Constraint::Default`
- `BOT_ORM::Constraint::Check`
- `BOT_ORM::Constraint::Unique`
- `BOT_ORM::Constraint::Reference`
## `ORMAP (TableName, PrimaryKey, ...)`
Before we use ORM Lite, we should **Inject** some code into the Class;
``` cpp
struct MyClass
{
int field1;
double field2;
std::string field3;
Nullable<int> field4;
Nullable<double> field5;
Nullable<std::string> field6;
// Inject ORM-Lite into this Class :-)
ORMAP ("TableName", field1, field2, field3,
field4, field5, field6);
};
```
In this sample, `ORMAP ("TableName", field1, ...)` specifies that:
- Class `MyClass` will be mapped into Table `TableName`;
- `field1, field2, field3, field4, field5, field6` will be mapped
into `INTEGER field1 NOT NULL`, `REAL field2 NOT NULL`,
`TEXT field3 NOT NULL`, `INTEGER field4`, `REAL field5`
and `TEXT field6` respectively;
- The first entry `field1` will be set as the **Primary Key**
of the Table;
Note that:
- You should Pass **at least 2** Params into this Macro
(TableName and PrimaryKey), otherwise it will Not Compile...;
- Currently Only Support
- T such that `std::is_integral<T>::value == true`
and **NOT** `char` or `*char_t`
- T such that `std::is_floating_point<T>::value == true`
- T such that `std::is_same<T, std::string>::value == true`
- which are mapped as `INTEGER`, `REAL` and `TEXT` (SQLite3);
- Not `Nullable` members will be mapped as `NOT NULL`;
- The **Primary Key** (first entry) is **Recommended** to be
`Integral`, and it could be regarded as the `ROWID`
with a Better Query **Performance** :-)
(otherwise SQLite 3 will Generate a Column for `ROWID` Implicitly)
- Field Names MUST **NOT** be SQL Keywords (SQL Constraint);
- `std::string` Value MUST **NOT** contain `\0` (SQL Constraint);
- `std::string` Value can be **utf-8** to support **Locale**;
- `ORMAP (...)` will **auto** Inject some **private members**;
- `__Accept ()` to Implement **Visitor Pattern**;
- `__Tuple ()` to **Flatten** data to tuple;
- `__FieldNames ()` and `__TableName` to store strings;
- and the Access by Implementation;
## `BOT_ORM::Nullable`
It keeps the *Similar Semantic* of `Nullable<T>` as `C#`; and
[Reference Here](https://stackoverflow.com/questions/2537942/nullable-values-in-c/28811646#28811646)
### Construction & Assignment
- Default Constructed / `nullptr` Constructed / `nullptr` Assigned
object is `NULL` Valued;
- Value Constructed / Value Assigned object is `NOT NULL` Valued
- `Nullable` Objects are **Copyable** / **Movable**,
and the *Destination* Value has the *Same Value* as the *Source*
``` cpp
// Default or Null Construction
Nullable ();
Nullable (nullptr_t);
// Null Assignment
const Nullable<T> & operator= (nullptr_t);
// Value Construction
Nullable (const T &value);
// Value Assignment
const Nullable<T> & operator= (const T &value);
```
### Get Value
Return the `Underlying Value` of the object or
`Default Not-null Value` of `T`;
(similar to `GetValueOrDefault` in C#)
``` cpp
const T &Value (); const
```
### Comparison
Two Objects have the Same value only if their `Nullable` Construction:
- Both are `NULL`;
- Both are `NOT NULL` and have the Same `Underlying Value`;
``` cpp
bool operator==(const Nullable<T> &op1, const Nullable<T> &op2);
bool operator==(const Nullable<T> &op1, T &op2);
bool operator==(T &op1, const Nullable<T> &op2);
bool operator==(const Nullable<T> &op1, nullptr_t);
bool operator==(nullptr_t, const Nullable<T> &op2);
```
## `BOT_ORM::ORMapper`
### Connection
``` cpp
ORMapper (const string &connectionString);
```
Remarks:
- Construct a **O/R Mapper** to connect to `connectionString`;
- For SQLite, the `connectionString` is the **database name**;
- The `ORMapper` **Keeps** the **Connection**
and **Shares** the **Connection** with `Queryable`;
- **Disconnecting** at all related `ORMapper`/`Queryable` destructied;
### Transaction
``` cpp
void Transaction (Fn fn);
```
Remarks:
- Invoke `fn` **Transactionally**, as following:
``` cpp
try
{
_connector.Execute ("begin transaction;");
fn ();
_connector.Execute ("commit transaction;");
}
catch (...)
{
_connector.Execute ("rollback transaction;");
throw;
}
```
### Create and Drop Table
``` cpp
// Create Table
void CreateTbl (const MyClass &);
void CreateTbl (const MyClass &,
const Constraint &constraint1,
const Constraint &constraint2,
...);
// Drop Table
void DropTbl (const MyClass &);
```
Remarks:
- Create/Drop Table for class `MyClass`;
- `void CreateTbl (const MyClass &, ...);`
will Create a Table with **Constraints**;
- `Constraint` will be described later;
SQL:
``` sql
CREATE TABLE MyClass (...);
DROP TABLE MyClass;
```
### Insert
``` cpp
// Insert a single value
void Insert (const MyClass &entity, bool withId = true);
// Insert values
void InsertRange (const Container<MyClass> &entities, bool withId = true);
```
Remarks:
- Insert `entity` / `entities` into Table for `MyClass`;
- If `withId` is `false`, it will insert the `entity`
without **Primary Key**;
- Note that: **Primary Key** is recommended to be **Integral**
in this case (**INT PK** would be `AUTOINCREMENT`,
Floating Point / String may **Failed**);
- **NULL** Fields will **NOT** be Set;
- `entities` must **SUPPORT** `forward_iterator`;
SQL:
``` sql
INSERT INTO MyClass (...) VALUES (...);
INSERT INTO MyClass (...) VALUES (...);
INSERT INTO MyClass (...) VALUES (...);
...
```
### Update
``` cpp
// Update value by Primary Key
void Update (const MyClass &entity);
// Update values by Primary Key
void UpdateRange (const Container<MyClass> &entities);
// Update by Expressions
void Update (const MyClass &,
const Expression::SetExpr &setExpr,
const Expression::Expr &whereExpr);
```
Remarks:
- Update `entity` / `entities` in Table `MyClass`
with the Same **Primary Key**;
- Update Set `setExpr` Where `whereExpr` for Table `MyClass`
(`Expressions` will be described later);
- **NULL** Fields will also be Set;
- `entities` must **SUPPORT** `forward_iterator`;
SQL:
``` sql
UPDATE MyClass SET (...) WHERE KEY = <entity.id>;
UPDATE MyClass SET (...) WHERE KEY = <entity.id>;
UPDATE MyClass SET (...) WHERE KEY = <entity.id>;
...
UPDATE MyClass SET (...) WHERE ...;
```
### Delete
``` cpp
// Delete value by Primary Key
void Delete (const MyClass &entity);
// Delete by Expressions
void Delete (const MyClass &,
const Expression::Expr &whereExpr);
```
Remarks:
- Delete Entry in Table `MyClass` with the Same **Primary Key**;
- Delete Where `whereExpr` for Table `MyClass`
(`Expression` will be described later);
- This function will **NOT** throw a `std::runtime_error`
even if there is nothing to delete;
SQL:
``` sql
DELETE FROM MyClass WHERE KEY = <entity.id>;
DELETE FROM MyClass WHERE ...;
```
### Query
``` cpp
// Retrieve a Queryable Object
Queryable<MyClass> Query (MyClass queryHelper);
```
Remarks:
- Return new `Queryable` object with `QueryResult` is `MyClass`;
- `MyClass` **MUST** be **Copy Constructible**
to Construct a `queryHelper`;
- The `ORMapper` **Shares** the **Connection** with `Queryable`;
## `BOT_ORM::Queryable<QueryResult>`
### Retrieve Results
``` cpp
Nullable<T> Aggregate (const Expression::Aggregate<T> &agg) const;
std::vector<QueryResult> ToVector () const;
std::list<QueryResult> ToList () const;
```
Remarks:
- `QueryResult` specifies the **Row Type** of Query Result;
- `Aggregate` will Get the one-or-zero-row Result for `agg`;
- `ToVector` / `ToList` returns the Collection of `QueryResult`;
- The results are from the **Connection** of `Queryable`;
- If the Result is `null` for `NOT Nullable` Field,
it will throw `std::runtime_error`;
- `Expression` will be described later;
### Set Conditions
``` cpp
Queryable Distinct (bool isDistinct = true) const;
Queryable Where (const Expression::Expr &expr) const;
Queryable GroupBy (const Expression::Field<T> &field) const;
Queryable Having (const Expression::Expr &expr) const;
Queryable OrderBy (const Expression::Field<T> &field) const;
Queryable OrderByDescending (const Expression::Field<T> &field) const;
Queryable Take (size_t count) const;
Queryable Skip (size_t count) const;
```
Remarks:
- Default Selection is `ALL`;
- These functions will Set/Append Conditions to a copy of `this`;
- `OrderBy` will **Append** `field` to Condition,
while Other functions will **Set** `DISTINCT`,
`expr`, `field` or `count` to Condition;
- New `Queryable` **Shares** the **Connection** of `this`;
- `Expression` will be described later;
### Construct New `QueryResult`
``` cpp
auto Select (const Expression::Selectable<T1> &target1,
const Expression::Selectable<T2> &target2,
...) const;
auto Join (const MyClass2 &queryHelper2,
const Expression::Expr &onExpr) const;
auto LeftJoin (const MyClass2 &queryHelper2,
const Expression::Expr &onExpr) const;
```
Remarks:
- Default Selection is **All Columns** (`SELECT *`);
- `Select` will Set `QueryResult` to `std::tuple<T1, T2, ...>`,
which can be retrieved by `SELECT target1, target2, ...`
(`target` can be **Field** or **Aggregate Functions**);
- `Join` / `LeftJoin` will Set `QueryResult` to `std::tuple<...>`
- `...` is the **flattened nullable concatenation** of all entries of
**Previous** `QueryResult` and `queryHelper2`;
- **Flatten** and **Nullable** means all fields of `...` are
all `Nullable<T>`, where `T` is the Supported Types of `ORMAP`
(NOT `std::tuple` or `MyClass`);
- `onExpr` specifies the `ON` Expression for `JOIN`;
- All Functions will copy the **Conditions** of `this` to the new one;
- New `Queryable` **Shares** the **Connection** of `this`;
- `Expression` will be described later;
### Compound Select
``` cpp
Queryable Union (const Queryable &queryable) const;
Queryable UnionAll (const Queryable &queryable) const;
Queryable Intersect (const Queryable &queryable) const;
Queryable Expect (const Queryable &queryable) const;
```
Remarks:
- All Functions will return a Compound Select
for `this` and `queryable`;
- All Functions will only **Inherit Conditions**
`OrderBy` and `Limit` from `this`;
- New `Queryable` **Shares** the **Connection** of `this`;
### Query SQL
We will use the following SQL to Query:
``` sql
SELECT [DISTINCT] ...
FROM TABLE
[LEFT] JOIN TABLE ON ...
...
WHERE ...
GROUP BY <field>
HAVING ...
[<Compound> SELECT ... FROM ...]
ORDER BY <field> [DESC], ...
LIMIT <take> OFFSET <skip>;
```
Remarks:
- If the Corresponding Condition is NOT Set, it will be omitted;
## `BOT_ORM::FieldExtractor`
``` cpp
// Construction
FieldExtractor (const MyClass1 &queryHelper1,
const MyClass2 &queryHelper2,
...);
// Get Field<> by operator ()
Field<T> operator () (const T &field) const;
NullableField<T> operator () (const Nullable<T> &field) const;
```
Remarks:
- Construction of `FieldExtractor` will take all fields' pointers of
`queryHelper` into a **Hash Table**;
- `operator () (field)` will find the position of `field`
in the **Hash Table** from `queryHelper`
and Construct the corresponding `Field`;
- If the `field` is `Nullable<T>`
it will Construct a `NullableField<T>`;
and it will Construct a `Field<T>` otherwise;
- If `field` is not a member of `queryHelper`,
it will throw `std::runtime_error`;
- `Expression` will be described later;
## `namespace BOT_ORM::Expression`
### Fields and Aggregate Functions
#### Definitions
``` cpp
BOT_ORM::Expression::Selectable<T>
BOT_ORM::Expression::Field<T> : public Selectable<T>
BOT_ORM::Expression::NullableField<T> : public Field<T>
BOT_ORM::Expression::Aggregate<T> : public Selectable<T>
```
#### Operations
``` cpp
// Field / Aggregate ? Value
Expr operator == (const Selectable<T> &op, T value);
Expr operator != (const Selectable<T> &op, T value);
Expr operator > (const Selectable<T> &op, T value);
Expr operator >= (const Selectable<T> &op, T value);
Expr operator < (const Selectable<T> &op, T value);
Expr operator <= (const Selectable<T> &op, T value);
// Field ? Field
Expr operator == (const Field<T> &op1, const Field<T> &op2);
Expr operator != (const Field<T> &op1, const Field<T> &op2);
Expr operator > (const Field<T> &op1, const Field<T> &op2);
Expr operator >= (const Field<T> &op1, const Field<T> &op2);
Expr operator < (const Field<T> &op1, const Field<T> &op2);
Expr operator <= (const Field<T> &op1, const Field<T> &op2);
// Nullable Field ? nullptr
Expr operator == (const NullableField<T> &op, nullptr_t);
Expr operator !== (const NullableField<T> &op, nullptr_t);
// String Field ? std::string
Expr operator& (const Field<std::string> &op, std::string val);
Expr operator| (const Field<std::string> &op, std::string val);
// Get SetExpr
SetExpr operator = (const Field<T> &op, T value);
SetExpr operator = (const NullableField<T> &op, nullptr_t);
```
Remarks:
- `Selectable<T> ? T` returns `Expr<op ? value>`;
- `Field<T> ? Field<T>` returns `Expr<op1 ? op2>`;
- `NullableField<T> == / != nullptr`
returns `Expr<op> IS NULL / IS NOT NULL`;
- `Field<std::string> & / | T`
returns `Expr<op> LIKE / NOT LIKE <value>`;
- `Field<T> = T` returns `SetExpr<op> = <value>`;
- `NullableField<T> = nullptr` returns `SetExpr<op> = null`;
### Expressions
#### Definitions
``` cpp
BOT_ORM::Expression::Expr
BOT_ORM::Expression::SetExpr
```
#### Operations
``` cpp
// Get Composite Expr
Expr operator && (const Expr &op1, const Expr &op2);
Expr operator || (const Expr &op1, const Expr &op2);
// Concatenate 2 SetExpr
SetExpr operator && (const SetExpr &op1, const SetExpr &op2);
```
Remarks:
- `Expr && / || Expr` returns `(<op1> and / or <op2>)`;
- `SetExpr && SetExpr` returns `<op1>, <op2>`;
### Aggregate Function Helpers
``` cpp
BOT_ORM::Expression::Count ();
BOT_ORM::Expression::Count (const Field<T> &field);
BOT_ORM::Expression::Sum (const Field<T> &field);
BOT_ORM::Expression::Avg (const Field<T> &field);
BOT_ORM::Expression::Max (const Field<T> &field);
BOT_ORM::Expression::Min (const Field<T> &field);
```
Remarks:
They will Generate Aggregate Functions as:
- `size_t COUNT (*)`
- `size_t COUNT (field)`
- `T SUM (field)`
- `T AVG (field)`
- `T MAX (field)`
- `T MIN (field)`
## `class BOT_ORM::Constraint`
### Composite Field
`CompositeField` could be constructed from normal `Field`
and is used by **Constraint Functions**;
``` cpp
CompositeField (const Expression::Field<T1> &field1,
const Expression::Field<T2> &field2,
...);
```
Remarks:
- Composite Field will Contain fields in the given **Order**;
- If the `field`s are not from the Same Table,
it will throw `std::runtime_error`;
### Generate Constraints
``` cpp
Constraint Default (const Expression::Field<T> &field,
const T &value);
Constraint Check (const Expression::Expr &expr);
Constraint Unique (const Expression::Field<T> &field);
Constraint Unique (const CompositeField &fields);
Constraint Reference(const Expression::Field<T> &field,
const Expression::Field<T> &refered);
Constraint Reference (const CompositeField &field,
const CompositeField &refered);
```
Remarks:
- They will Generate Constraints as:
`DEFAULT`, `CHECK`, `UNIQUE` and `FOREIGN KEY`;
- `FOREIGN KEY` is Enabled by Default
(during the Construction of `ORMapper`);
- Why there is NO **Composite Primary Key** Constaints:
- It's recommended to use a **Integral** Field as the Primary Key,
described in Section ## Macro `ORMAP`;
- We can use `Unique` to implement a **Composite Unique** Constraint;
- And use `Reference` to define a **Composite Foreign Key**;
## Error Handling
### Compile-time Error
ORM Lite uses `static_assert` to Check if the Code is valid:
- **Forget** to **Place** `ORMAP` into the Class
> Please Inject the Class with 'ORMAP' first
- Place **Unsupported Types** into `ORMAP`
> Only Support Integral, Floating Point and std::string
Note that: Error Messages will often appear at the **TOP**;
### Runtime Error
All Functions will throw `std::runtime_error`
with the **Error Message** if Failed:
- Failed to **Connect** to **Database**
> SQL error: Can't open database `<connectionString>`
- Failed at Executing **Query** Script
> SQL error: `<ErrorMessage>` at `<Generated SQL Script>`
- Query Result's **Column Count** does **NOT Match** the Expected Count
(happening in **NOT** *Code First* Cases...)
> SQL error: Bad Column Count at `<Generated SQL Script>`
- Get `NULL` from Query while the Expected **Field** is **NOT NULL**
(happening in **NOT** *Code First* Cases...)
> SQL error: Get Null Value at `<Generated SQL Script>`
- Pass a **Non-Member** Var of Registered Object to Field **Extractor**
> No Such Field for current Extractor
- **Composite** Fields from **NOT** the Same Tables
> Fields are NOT from the Same Table
================================================
FILE: sample/makefile
================================================
TARGET = sample
SOURCES = sample.cpp ../src/sqlite3.c
WARNINGFLAGS = -Wall -W
CPPFLAGS = -std=c++14
LINKS = -lstdc++ -lpthread -ldl
OBJS = $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES)))
%.o: %.c
$(CC) -c $< -o $@ $(WARNINGFLAGS) $(LINKS)
%.o: %.cpp
$(CC) -c $< -o $@ $(WARNINGFLAGS) $(LINKS) $(CPPFLAGS)
$(TARGET): $(OBJS)
$(CC) $(OBJS) -o $(TARGET) $(LINKS)
clean:
rm -rf $(OBJS) $(TARGET) *.db
================================================
FILE: sample/sample.cpp
================================================
// Sample of ORM Lite
// https://github.com/BOT-Man-JL/ORM-Lite
// BOT Man, 2016
#include <string>
#include <iostream>
#include <tuple>
#include "../src/ormlite.h"
using namespace BOT_ORM;
using namespace BOT_ORM::Expression;
namespace PrintHelper
{
template<class T>
void PrintNullable (const BOT_ORM::Nullable<T> &val)
{
if (val == nullptr)
std::cout << "null";
else
std::cout << val.Value ();
}
template<class Tuple, std::size_t N>
struct TuplePrinter
{
static void print (const Tuple& t)
{
TuplePrinter<Tuple, N - 1>::print (t);
std::cout << ", ";
PrintNullable (std::get<N - 1> (t));
}
};
template<class Tuple>
struct TuplePrinter<Tuple, 1>
{
static void print (const Tuple& t)
{
PrintNullable (std::get<0> (t));
}
};
template<class... Args>
void PrintTuple (const std::tuple<Args...>& t)
{
std::cout << "(";
TuplePrinter<decltype(t), sizeof...(Args)>::print (t);
std::cout << ")\n";
}
template<class Container>
void PrintTuples (const Container &vals)
{
for (const auto &val : vals)
PrintTuple (val);
std::cout << std::endl;
}
}
struct UserModel
{
int user_id;
std::string user_name;
double credit_count;
Nullable<int> age;
Nullable<double> salary;
Nullable<std::string> title;
// Inject ORM-Lite into this Class :-)
ORMAP ("UserModel", user_id, user_name, credit_count, age, salary, title);
};
struct SellerModel
{
int seller_id;
std::string seller_name;
double credit_count;
// Inject ORM-Lite into this Class :-)
ORMAP ("SellerModel", seller_id, seller_name, credit_count);
};
struct OrderModel
{
int order_id;
int user_id;
int seller_id;
std::string product_name;
Nullable<double> fee;
// Inject ORM-Lite into this Class :-)
ORMAP ("OrderModel", order_id, user_id, seller_id, product_name, fee);
};
int main ()
{
/*
## Field Extracting
*/
// Define more Query Helper Objects and their Field Extractor
UserModel user;
SellerModel seller;
OrderModel order;
auto field = FieldExtractor { user, seller, order };
// Extract Field from 'field'
// For example: field (user.user_name)
// => Retrieve the field of user_name in UserModel table
/*
## Working on *Database* with *ORMapper*
*/
// Open a Connection with 'sample.db'
ORMapper mapper ("sample.db");
/*
## Create or Drop Tables
*/
try
{
// Drop Tables
mapper.DropTbl (OrderModel {});
mapper.DropTbl (UserModel {});
mapper.DropTbl (SellerModel {});
}
catch (...) {}
// Create Table with Constraints
mapper.CreateTbl (
UserModel {},
Constraint::Default (field (user.salary), 1000.0));
// Remarks:
// CREATE TABLE UserModel(
// user_id INTEGER NOT NULL PRIMARY KEY,
// user_name TEXT NOT NULL,
// credit_count REAL NOT NULL,
// age INTEGER,
// salary REAL DEFAULT 1000,
// title TEXT);
mapper.CreateTbl (
SellerModel {},
Constraint::Check (field (seller.credit_count) > 0.0));
// Remarks:
// CREATE TABLE SellerModel(
// seller_id INTEGER NOT NULL PRIMARY KEY,
// seller_name TEXT NOT NULL,
// credit_count REAL NOT NULL,
// CHECK (credit_count > 0));
mapper.CreateTbl (
OrderModel {},
Constraint::Reference (
field (order.user_id), field (user.user_id)),
Constraint::Reference (
field (order.seller_id), field (seller.seller_id)),
Constraint::Unique (Constraint::CompositeField {
field (order.product_name), field (order.fee) }));
// Remarks:
// CREATE TABLE OrderModel(
// order_id INTEGER NOT NULL PRIMARY KEY,
// user_id INTEGER NOT NULL,
// seller_id INTEGER NOT NULL,
// product_name TEXT NOT NULL,
// fee REAL,
// FOREIGN KEY (user_id) REFERENCES UserModel(user_id),
// FOREIGN KEY (seller_id) REFERENCES SellerModel(seller_id),
// UNIQUE (product_name, fee));
/*
## Basic CRUD
*/
std::vector<UserModel> initObjs =
{
{ 0, "John", 0.2, 21, nullptr, nullptr },
{ 1, "Jack", 0.4, nullptr, 3.14, nullptr },
{ 2, "Jess", 0.6, nullptr, nullptr, std::string ("Dr.") }
};
// Insert Values with Primary Key
for (const auto &obj : initObjs)
mapper.Insert (obj);
initObjs[1].salary = nullptr;
initObjs[1].title = "St.";
// Update Entity by Primary Key (WHERE UserModel.id = 1)
mapper.Update (initObjs[1]);
// Delete Entity by Primary Key (WHERE UserModel.id = 2)
mapper.Delete (initObjs[2]);
// Transactional Statements
try
{
mapper.Transaction ([&] ()
{
mapper.Delete (initObjs[0]); // OK
mapper.Insert (UserModel {
1, "Joke", 0, nullptr, nullptr, nullptr
}); // Failed
});
}
catch (const std::exception &ex)
{
// If any statement Failed, throw an exception
std::cout << ex.what () << std::endl;
// SQL error: 'UNIQUE constraint failed: UserModel.id'
// Remarks:
// mapper.Delete (initObjs[0]); will not applied :-)
}
// Select All to List
auto result1 = mapper.Query (UserModel {}).ToList ();
// decltype (result1) == std::list<UserModel>
// result1 = [{ 0, 0.2, "John", 21, 1000, null },
// { 1, 0.4, "Jack", null, null, "St." }]
// Table Constraints
try
{
// Insert Values without Primary Key
mapper.Insert (SellerModel { 0, "John Inc.", 0.0 }, false);
}
catch (const std::exception &ex)
{
std::cout << ex.what () << std::endl;
// SQL error: 'CHECK constraint failed: SellerModel'
}
/*
## Batch Operations
*/
std::vector<UserModel> dataToSeed;
for (int i = 50; i < 100; i++)
dataToSeed.emplace_back (UserModel {
i, "July_" + std::to_string (i), i * 0.2,
nullptr, nullptr, nullptr
});
// Insert by Batch Insert
mapper.Transaction ([&] () {
mapper.InsertRange (dataToSeed);
});
for (size_t i = 0; i < 20; i++)
{
dataToSeed[i + 30].age = 30 + (int) i / 2;
dataToSeed[i + 20].title = "Mr. " + std::to_string (i);
}
// Update by Batch Update
mapper.Transaction ([&] () {
// Note that: it will erase the default value of 'salary'
mapper.UpdateRange (dataToSeed);
});
/*
## Single-Table Query
*/
// Select by Condition
auto result2 = mapper.Query (UserModel {})
.Where (
field (user.user_name) & std::string ("July%") &&
(field (user.age) >= 32 &&
field (user.title) != nullptr)
)
.OrderByDescending (field (user.age))
.OrderBy (field (user.user_id))
.Take (3)
.Skip (1)
.ToVector ();
// Remarks:
// SELECT * FROM UserModel
// WHERE (user_name LIKE 'July%' AND
// (age >= 32 AND title IS NOT NULL))
// ORDER BY age DESC, user_id
// LIMIT 3 OFFSET 1
// decltype (result2) == std::vector<UserModel>
// result2 = [{ 89, 17.8, "July_89", 34, null, "Mr. 19" },
// { 86, 17.2, "July_86", 33, null, "Mr. 16" },
// { 87, 17.4, "July_87", 33, null, "Mr. 17" }]
// Calculate Aggregate Function
auto avg = mapper.Query (UserModel {})
.Where (field (user.user_name) & std::string ("July%"))
.Aggregate (Avg (field (user.credit_count)));
// Remarks:
// SELECT AVG (credit_count) FROM UserModel
// WHERE (user_name LIKE 'July%')
// avg = 14.9
auto count = mapper.Query (UserModel {})
.Where (field (user.user_name) | std::string ("July%"))
.Aggregate (Count ());
// Remarks:
// SELECT COUNT (*) FROM UserModel
// WHERE (user_name NOT LIKE 'July%')
// count = 2
/*
## Update / Delete by Statement
*/
// Update by Condition
mapper.Update (
UserModel {},
(field (user.age) = 10) &&
(field (user.credit_count) = 1.0),
field (user.user_name) == std::string ("July"));
// Remarks:
// UPDATE UserModel SET age = 10,credit_count = 1.0
// WHERE (user_name = 'July')
// Delete by Condition
mapper.Delete (UserModel {},
field (user.user_id) >= 90);
// Remarks:
// DELETE FROM UserModel WHERE (id >= 90)
/*
## Multi-Table Query
*/
mapper.Transaction ([&] ()
{
for (size_t i = 0; i < 50; i++)
{
mapper.Insert (
SellerModel { (int) i + 50,
"Seller " + std::to_string (i), 3.14 });
mapper.Insert (
OrderModel { 0,
(int) i / 2 + 50,
(int) i / 4 + 50,
"Item " + std::to_string (i),
i * 0.5 }, false);
}
});
// Join Tables for Query
auto joinedQuery = mapper.Query (UserModel {})
.Join (OrderModel {},
field (user.user_id) ==
field (order.user_id))
.LeftJoin (SellerModel {},
field (seller.seller_id) ==
field (order.seller_id))
.Where (field (user.user_id) >= 65);
// Get Result to List
auto result3 = joinedQuery.ToList ();
// Remarks:
// SELECT * FROM UserModel
// JOIN OrderModel
// ON UserModel.user_id=OrderModel.user_id
// LEFT JOIN SellerModel
// ON SellerModel.seller_id=OrderModel.seller_id
// WHERE (UserModel.user_id>=65)
// decltype (result3) == std::list<std::tuple<Nullable<..>, ..>>
// result3 = [(65, "July_65", 13, null, null, null,
// 31, 65, 57, "Item 30", 15,
// 57, "Seller 7", 3.14),
// (65, "July_65", 13, null, null, null,
// 32, 65, 57, "Item 31", 15.5,
// 57, "Seller 7", 3.14),
// ... ]
// Group & Having ~
auto result4 = joinedQuery
.Select (field (order.user_id),
field (user.user_name),
Avg (field (order.fee)))
.GroupBy (field (user.user_name))
.Having (Sum (field (order.fee)) >= 40.5)
.Skip (3)
.ToList ();
// Remarks:
// SELECT OrderModel.user_id,
// UserModel.user_name,
// AVG (OrderModel.fee)
// FROM UserModel
// JOIN OrderModel
// ON UserModel.user_id=OrderModel.user_id
// LEFT JOIN SellerModel
// ON SellerModel.seller_id=OrderModel.seller_id
// WHERE (UserModel.user_id>=65)
// GROUP BY UserModel.user_name
// HAVING SUM (OrderModel.fee)>=40.5
// LIMIT ~0 OFFSET 3
// decltype (result4) == std::list<std::tuple<Nullable<..>, ..>>
// result4 = [(73, "July_73", 23.25),
// (74, "July_74", 24.25)]
// Compound Select
// Results are Nullable-Tuples
auto result5 = mapper.Query (OrderModel {})
.Select (field (order.product_name), field (order.user_id))
.Where (field (order.user_id) == 50)
.Union (
joinedQuery
.Select (field (user.user_name), field (order.order_id))
)
.Take (4)
.ToList ();
// Remarks:
// SELECT OrderModel.product_name,
// OrderModel.user_id
// FROM OrderModel
// WHERE (OrderModel.user_id==50)
// UNION
// SELECT UserModel.user_name,
// OrderModel.order_id
// FROM UserModel
// JOIN OrderModel
// ON UserModel.user_id=OrderModel.user_id
// LEFT JOIN SellerModel
// ON SellerModel.seller_id=OrderModel.seller_id
// WHERE (UserModel.user_id>=65)
// LIMIT 4;
// decltype (result5) == std::list<std::tuple<Nullable<..>, ..>>
// result5 = [("Item 0", 50),
// ("Item 1", 50),
// ("July_65", 31),
// ("July_65", 32)]
// ====================================================
// Output UserModel Objects
auto printUserModeles = [] (const auto &objs)
{
for (const auto& item : objs)
{
std::cout << item.user_id << "\t" << item.credit_count
<< "\t" << item.user_name << "\t";
PrintHelper::PrintNullable (item.age);
std::cout << "\t";
PrintHelper::PrintNullable (item.salary);
std::cout << "\t";
PrintHelper::PrintNullable (item.title);
std::cout << "\n";
}
std::cout << std::endl;
};
// Sec 1
std::cout << "\n";
printUserModeles (result1);
// Sec 2
printUserModeles (result2);
PrintHelper::PrintNullable (count);
std::cout << "\n";
PrintHelper::PrintNullable (avg);
std::cout << "\n" << std::endl;
// Sec 3
PrintHelper::PrintTuples (result3);
PrintHelper::PrintTuples (result4);
PrintHelper::PrintTuples (result5);
//std::cin.get ();
return 0;
}
================================================
FILE: sample/sample.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
1781A76A1F5FF9EE008E6067 /* sample.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1781A7691F5FF9EE008E6067 /* sample.cpp */; };
1781A76F1F5FF9FC008E6067 /* sqlite3.c in Sources */ = {isa = PBXBuildFile; fileRef = 1781A76D1F5FF9FC008E6067 /* sqlite3.c */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
1781A75D1F5FF992008E6067 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1781A75F1F5FF992008E6067 /* sample */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = sample; sourceTree = BUILT_PRODUCTS_DIR; };
1781A7691F5FF9EE008E6067 /* sample.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sample.cpp; path = sample.cpp; sourceTree = "<group>"; };
1781A76B1F5FF9FC008E6067 /* nullable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nullable.h; path = ../src/nullable.h; sourceTree = "<group>"; };
1781A76C1F5FF9FC008E6067 /* ormlite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ormlite.h; path = ../src/ormlite.h; sourceTree = "<group>"; };
1781A76D1F5FF9FC008E6067 /* sqlite3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sqlite3.c; path = ../src/sqlite3.c; sourceTree = "<group>"; };
1781A76E1F5FF9FC008E6067 /* sqlite3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sqlite3.h; path = ../src/sqlite3.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
1781A75C1F5FF992008E6067 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
1781A7561F5FF992008E6067 = {
isa = PBXGroup;
children = (
1781A76B1F5FF9FC008E6067 /* nullable.h */,
1781A76C1F5FF9FC008E6067 /* ormlite.h */,
1781A76D1F5FF9FC008E6067 /* sqlite3.c */,
1781A76E1F5FF9FC008E6067 /* sqlite3.h */,
1781A7691F5FF9EE008E6067 /* sample.cpp */,
1781A7601F5FF992008E6067 /* Products */,
);
sourceTree = "<group>";
};
1781A7601F5FF992008E6067 /* Products */ = {
isa = PBXGroup;
children = (
1781A75F1F5FF992008E6067 /* sample */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
1781A75E1F5FF992008E6067 /* sample */ = {
isa = PBXNativeTarget;
buildConfigurationList = 1781A7661F5FF992008E6067 /* Build configuration list for PBXNativeTarget "sample" */;
buildPhases = (
1781A75B1F5FF992008E6067 /* Sources */,
1781A75C1F5FF992008E6067 /* Frameworks */,
1781A75D1F5FF992008E6067 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = sample;
productName = sample;
productReference = 1781A75F1F5FF992008E6067 /* sample */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
1781A7571F5FF992008E6067 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0830;
TargetAttributes = {
1781A75E1F5FF992008E6067 = {
CreatedOnToolsVersion = 8.3.3;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 1781A75A1F5FF992008E6067 /* Build configuration list for PBXProject "sample" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 1781A7561F5FF992008E6067;
productRefGroup = 1781A7601F5FF992008E6067 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
1781A75E1F5FF992008E6067 /* sample */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
1781A75B1F5FF992008E6067 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1781A76A1F5FF9EE008E6067 /* sample.cpp in Sources */,
1781A76F1F5FF9FC008E6067 /* sqlite3.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
1781A7641F5FF992008E6067 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.12;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
1781A7651F5FF992008E6067 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.12;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
};
name = Release;
};
1781A7671F5FF992008E6067 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
1781A7681F5FF992008E6067 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
1781A75A1F5FF992008E6067 /* Build configuration list for PBXProject "sample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1781A7641F5FF992008E6067 /* Debug */,
1781A7651F5FF992008E6067 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
1781A7661F5FF992008E6067 /* Build configuration list for PBXNativeTarget "sample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1781A7671F5FF992008E6067 /* Debug */,
1781A7681F5FF992008E6067 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 1781A7571F5FF992008E6067 /* Project object */;
}
================================================
FILE: sample/sample.vcxproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{AC415D81-EA1D-43F3-A741-60ED85136D48}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>sample</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\sample\</OutDir>
<IntDir>$(Configuration)\sample\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(Platform)\$(Configuration)\sample\</IntDir>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\sample\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalOptions>/std:c++14 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\sqlite3.c" />
<ClCompile Include="sample.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\ormlite.h" />
<ClInclude Include="..\src\nullable.h" />
<ClInclude Include="..\src\sqlite3.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
================================================
FILE: src/nullable.h
================================================
// Nullable module for ORM Lite
// ORM Lite - An ORM for SQLite in C++ 17
// https://github.com/BOT-Man-JL/ORM-Lite
// BOT Man, 2017
#ifndef BOT_ORM_NULLABLE_H
#define BOT_ORM_NULLABLE_H
// std::nullptr_t
#include <cstddef>
// Nullable Template
// https://stackoverflow.com/questions/2537942/nullable-values-in-c/28811646#28811646
namespace BOT_ORM
{
template <typename T>
class Nullable
{
template <typename T2>
friend bool operator== (const Nullable<T2> &op1,
const Nullable<T2> &op2);
template <typename T2>
friend bool operator== (const Nullable<T2> &op,
const T2 &value);
template <typename T2>
friend bool operator== (const T2 &value,
const Nullable<T2> &op);
template <typename T2>
friend bool operator== (const Nullable<T2> &op,
std::nullptr_t);
template <typename T2>
friend bool operator== (std::nullptr_t,
const Nullable<T2> &op);
public:
// Default or Null Construction
Nullable ()
: m_hasValue (false), m_value (T ())
{}
Nullable (std::nullptr_t)
: Nullable ()
{}
// Null Assignment
const Nullable<T> & operator= (std::nullptr_t)
{
m_hasValue = false;
m_value = T ();
return *this;
}
// Value Construction
template<typename T2>
Nullable (const T2 &value)
: m_hasValue (true), m_value (value)
{}
// Value Assignment
template<typename T2>
const Nullable<T> & operator= (const T2 &value)
{
m_hasValue = true;
m_value = value;
return *this;
}
private:
bool m_hasValue;
T m_value;
public:
const T &Value () const
{
return m_value;
}
};
// == varialbe
template <typename T2>
inline bool operator== (const Nullable<T2> &op1,
const Nullable<T2> &op2)
{
return op1.m_hasValue == op2.m_hasValue &&
(!op1.m_hasValue || op1.m_value == op2.m_value);
}
// == value
template <typename T2>
inline bool operator== (const Nullable<T2> &op,
const T2 &value)
{
return op.m_hasValue && op.m_value == value;
}
template <typename T2>
inline bool operator== (const T2 &value,
const Nullable<T2> &op)
{
return op.m_hasValue && op.m_value == value;
}
// == nullptr
template <typename T2>
inline bool operator== (const Nullable<T2> &op,
std::nullptr_t)
{
return !op.m_hasValue;
}
template <typename T2>
inline bool operator== (std::nullptr_t,
const Nullable<T2> &op)
{
return !op.m_hasValue;
}
}
#endif // !BOT_ORM_NULLABLE_H
================================================
FILE: src/ormlite.h
================================================
// ORM Lite
// An ORM for SQLite in C++ 14
// https://github.com/BOT-Man-JL/ORM-Lite
// BOT Man, 2017
#ifndef BOT_ORM_H
#define BOT_ORM_H
// Container
#include <tuple>
#include <vector>
#include <list>
#include <string>
#include <unordered_map>
// Serialization
#include <sstream>
// Type Traits
#include <type_traits>
// std::shared_ptr
#include <memory>
// for Field Name Extractor
#include <cctype>
// for SQL Connector
#include <thread>
#include <functional>
// SQLite 3 Dependency
#include "sqlite3.h"
// Nullable Module
#include "nullable.h"
// Public Macro
#define ORMAP(_TABLE_NAME_, ...) \
private: \
friend class BOT_ORM_Impl::InjectionHelper; \
template <typename FN> \
inline decltype (auto) __Accept (FN fn) \
{ return fn (__VA_ARGS__); } \
template <typename FN> \
inline decltype (auto) __Accept (FN fn) const \
{ return fn (__VA_ARGS__); } \
constexpr static const char *__FieldNames = #__VA_ARGS__; \
constexpr static const char *__TableName = _TABLE_NAME_; \
// Private Macros
#define NO_ORMAP "Please Inject the Class with 'ORMAP' first"
#define BAD_TYPE "Only Support Integral, Floating Point and std::string"
#define BAD_COLUMN_COUNT "Bad Column Count"
#define NULL_DESERIALIZE "Get Null Value"
#define NO_FIELD "No Such Field for current Extractor"
#define NOT_SAME_TABLE "Fields are NOT from the Same Table"
// Helpers
namespace BOT_ORM_Impl
{
// Naive SQL Driver (Todo: Improved Later)
class SQLConnector
{
public:
SQLConnector (const std::string &fileName)
{
if (sqlite3_open (fileName.c_str (), &db) != SQLITE_OK)
throw std::runtime_error (
std::string ("SQL error: Can't open database '")
+ sqlite3_errmsg (db) + "'");
}
~SQLConnector ()
{
sqlite3_close (db);
}
void Execute (const std::string &cmd)
{
char *zErrMsg = 0;
int rc = SQLITE_OK;
for (size_t iTry = 0; iTry < MAX_TRIAL; iTry++)
{
rc = sqlite3_exec (db, cmd.c_str (), 0, 0, &zErrMsg);
if (rc != SQLITE_BUSY)
break;
std::this_thread::sleep_for (
std::chrono::microseconds (20));
}
if (rc != SQLITE_OK)
{
auto errStr = std::string ("SQL error: '") + zErrMsg
+ "' at '" + cmd + "'";
sqlite3_free (zErrMsg);
throw std::runtime_error (errStr);
}
}
void ExecuteCallback (const std::string &cmd,
std::function<void (int, char **)>
callback)
{
char *zErrMsg = 0;
int rc = SQLITE_OK;
auto callbackParam = std::make_pair (&callback, std::string {});
for (size_t iTry = 0; iTry < MAX_TRIAL; iTry++)
{
rc = sqlite3_exec (db, cmd.c_str (), CallbackWrapper,
&callbackParam, &zErrMsg);
if (rc != SQLITE_BUSY)
break;
std::this_thread::sleep_for (
std::chrono::microseconds (20));
}
if (rc == SQLITE_ABORT)
{
auto errStr = "SQL error: '" + callbackParam.second +
"' at '" + cmd + "'";
sqlite3_free (zErrMsg);
throw std::runtime_error (errStr);
}
else if (rc != SQLITE_OK)
{
auto errStr = std::string ("SQL error: '") + zErrMsg
+ "' at '" + cmd + "'";
sqlite3_free (zErrMsg);
throw std::runtime_error (errStr);
}
}
private:
sqlite3 *db;
constexpr static size_t MAX_TRIAL = 16;
static int CallbackWrapper (
void *callbackParam, int argc, char **argv, char **)
{
auto pParam = static_cast<std::pair<
std::function<void (int, char **)> *,
std::string
>*>(callbackParam);
try
{
pParam->first->operator()(argc, argv);
return 0;
}
catch (const std::exception &ex)
{
pParam->second = ex.what ();
return 1;
}
}
};
// Helper - Field Type Checker
template <typename T>
struct TypeString
{
constexpr static const char *typeStr =
(std::is_integral<T>::value &&
!std::is_same<T, char>::value &&
!std::is_same<T, wchar_t>::value &&
!std::is_same<T, char16_t>::value &&
!std::is_same<T, char32_t>::value &&
!std::is_same<T, unsigned char>::value)
? " integer not null"
: (std::is_floating_point<T>::value)
? " real not null"
: (std::is_same<T, std::string>::value)
? " text not null"
: nullptr;
static_assert (typeStr != nullptr, BAD_TYPE);
};
template <typename T>
struct TypeString <BOT_ORM::Nullable<T>>
{
constexpr static const char *typeStr =
(std::is_integral<T>::value &&
!std::is_same<T, char>::value &&
!std::is_same<T, wchar_t>::value &&
!std::is_same<T, char16_t>::value &&
!std::is_same<T, char32_t>::value &&
!std::is_same<T, unsigned char>::value)
? " integer"
: (std::is_floating_point<T>::value)
? " real"
: (std::is_same<T, std::string>::value)
? " text"
: nullptr;
static_assert (typeStr != nullptr, BAD_TYPE);
};
// Serialization Helper
struct SerializationHelper
{
template <typename T>
static inline
std::enable_if_t<TypeString<T>::typeStr == nullptr, bool>
Serialize (std::ostream &, const T &)
{}
template <typename T>
static inline
std::enable_if_t<TypeString<T>::typeStr != nullptr, bool>
Serialize (std::ostream &os, const T &value)
{
os << value;
return true;
}
static inline bool Serialize (std::ostream &os,
const std::string &value)
{
os << "'" << value << "'";
return true;
}
template <typename T>
static inline bool Serialize (
std::ostream &os,
const BOT_ORM::Nullable<T> &value)
{
if (value == nullptr)
return false;
return Serialize (os, value.Value ());
}
};
// Deserialization Helper
struct DeserializationHelper
{
template <typename T>
static inline std::enable_if_t<TypeString<T>::typeStr == nullptr>
Deserialize (T &, const char *)
{}
template <typename T>
static inline std::enable_if_t<TypeString<T>::typeStr != nullptr>
Deserialize (T &property, const char *value)
{
if (value) std::istringstream { value } >> property;
else throw std::runtime_error (NULL_DESERIALIZE);
}
static inline void Deserialize (std::string &property,
const char *value)
{
if (value) property = value;
else throw std::runtime_error (NULL_DESERIALIZE);
}
template <typename T>
static inline void Deserialize (
BOT_ORM::Nullable<T> &property, const char *value)
{
if (value)
{
T val;
Deserialize (val, value);
property = val;
}
else
property = nullptr;
}
};
// Injection Helper
class InjectionHelper
{
static std::vector<std::string> ExtractFieldName (std::string input)
{
std::vector<std::string> ret;
std::string tmpStr;
for (char ch : std::move (input) + ",")
{
if (isalnum (ch) || ch == '_')
tmpStr += ch;
else if (ch == ',')
{
if (!tmpStr.empty ())
ret.push_back (tmpStr);
tmpStr.clear ();
}
}
return ret;
};
public:
// Checking Injection
template <typename T> class HasInjected
{
template <typename...> struct make_void { using type = void; };
template <typename... _Types>
using void_t = typename make_void<_Types...>::type;
template <typename, typename = void_t<>>
struct Test : std::false_type {};
template <typename U>
struct Test <U, void_t<decltype (U::__TableName)>>
: std::true_type
{};
public:
static constexpr bool value = Test<T>::value;
static_assert (value, NO_ORMAP);
};
// Proxy Function
template <typename C, typename Fn>
static inline decltype (auto) Visit (C &obj, Fn fn)
{
return obj.__Accept (fn);
}
// Field Name Proxy
template <typename C>
static inline const std::vector<std::string> &FieldNames (const C &)
{
static const auto fieldNames = ExtractFieldName (C::__FieldNames);
return fieldNames;
}
// Table Name Proxy
template <typename C>
static inline const std::string &TableName (const C &)
{
static const std::string tableName { C::__TableName };
return tableName;
}
};
// Unpacking Tricks :-)
// http://stackoverflow.com/questions/26902633/how-to-iterate-over-a-tuple-in-c-11/26902803#26902803
// - To avoid the unspecified order,
// brace-enclosed initializer lists can be used,
// which guarantee strict left-to-right order of evaluation.
// - To avoid the need for a not void return type,
// the comma operator can be used to
// always yield 1 in each expansion element.
using Expander = int[];
}
namespace BOT_ORM
{
namespace Expression
{
// SetExpr
struct SetExpr
{
SetExpr (std::string field_op_val)
: _expr { std::move (field_op_val) }
{}
const std::string &ToString () const
{
return _expr;
}
inline SetExpr operator && (const SetExpr &rhs) const
{
return SetExpr { _expr + "," + rhs._expr };
}
private:
std::string _expr;
};
// Selectable
template <typename T>
struct Selectable
{
std::string fieldName;
const std::string *tableName;
Selectable (std::string _fieldName,
const std::string *_tableName)
: fieldName (std::move (_fieldName)), tableName (_tableName)
{}
};
// Field : Selectable
template <typename T>
struct Field : public Selectable<T>
{
Field (std::string _fieldName,
const std::string *_tableName)
: Selectable<T> (std::move (_fieldName), _tableName) {}
inline SetExpr operator = (T value)
{
std::ostringstream os;
BOT_ORM_Impl::SerializationHelper::
Serialize (os << this->fieldName << "=", value);
return SetExpr { os.str () };
}
};
// Nullable Field : Field : Selectable
template <typename T>
struct NullableField : public Field<T>
{
NullableField (std::string _fieldName,
const std::string *_tableName)
: Field<T> (std::move (_fieldName), _tableName) {}
inline SetExpr operator = (T value)
{
std::ostringstream os;
BOT_ORM_Impl::SerializationHelper::
Serialize (os << this->fieldName << "=", value);
return SetExpr { os.str () };
}
inline SetExpr operator = (std::nullptr_t)
{
return SetExpr { this->fieldName + "=null" };
}
};
// Aggregate Function : Selectable
template <typename T>
struct Aggregate : public Selectable<T>
{
Aggregate (std::string function)
: Selectable<T> (std::move (function), nullptr) {}
Aggregate (std::string function, const Field<T> &field)
: Selectable<T> (std::move (function) + "(" +
*(field.tableName) + "." +
field.fieldName + ")", nullptr)
{}
};
// Expr
struct Expr
{
template <typename T>
Expr (const Selectable<T> &field, std::string op_val)
: _exprs { { field.fieldName + op_val, field.tableName } }
{}
template <typename T>
Expr (const Selectable<T> &field, std::string op, T value)
{
std::ostringstream os;
BOT_ORM_Impl::SerializationHelper::
Serialize (os << field.fieldName << op, value);
_exprs.emplace_back (os.str (), field.tableName);
}
template <typename T>
Expr (const Field<T> &field1,
std::string op,
const Field<T> &field2)
: _exprs
{
{ field1.fieldName, field1.tableName },
{ std::move (op), nullptr },
{ field2.fieldName, field2.tableName }
}
{}
std::string ToString () const
{
std::ostringstream out;
for (const auto &expr : _exprs)
{
if (expr.second != nullptr)
out << *(expr.second) << ".";
out << expr.first;
}
return out.str ();
}
inline Expr operator && (const Expr &rhs) const
{
return And_Or (rhs, " and ");
}
inline Expr operator || (const Expr &rhs) const
{
return And_Or (rhs, " or ");
}
private:
std::list<std::pair<std::string, const std::string *>> _exprs;
Expr And_Or (const Expr &rhs, std::string logOp) const
{
auto ret = *this;
auto rigthExprs = rhs._exprs;
ret._exprs.emplace_front ("(", nullptr);
ret._exprs.emplace_back (std::move (logOp), nullptr);
ret._exprs.splice (ret._exprs.cend (),
std::move (rigthExprs));
ret._exprs.emplace_back (")", nullptr);
return ret;
}
};
// Field / Aggregate ? Value
template <typename T>
inline Expr operator == (const Selectable<T> &op, T value)
{
return Expr (op, "=", std::move (value));
}
template <typename T>
inline Expr operator != (const Selectable<T> &op, T value)
{
return Expr (op, "!=", std::move (value));
}
template <typename T>
inline Expr operator > (const Selectable<T> &op, T value)
{
return Expr (op, ">", std::move (value));
}
template <typename T>
inline Expr operator >= (const Selectable<T> &op, T value)
{
return Expr (op, ">=", std::move (value));
}
template <typename T>
inline Expr operator < (const Selectable<T> &op, T value)
{
return Expr (op, "<", std::move (value));
}
template <typename T>
inline Expr operator <= (const Selectable<T> &op, T value)
{
return Expr (op, "<=", std::move (value));
}
// Field ? Field
template <typename T>
inline Expr operator == (const Field<T> &op1, const Field<T> &op2)
{
return Expr { op1 , "=", op2 };
}
template <typename T>
inline Expr operator != (const Field<T> &op1, const Field<T> &op2)
{
return Expr { op1, "!=", op2 };
}
template <typename T>
inline Expr operator > (const Field<T> &op1, const Field<T> &op2)
{
return Expr { op1 , ">", op2 };
}
template <typename T>
inline Expr operator >= (const Field<T> &op1, const Field<T> &op2)
{
return Expr { op1, ">=", op2 };
}
template <typename T>
inline Expr operator < (const Field<T> &op1, const Field<T> &op2)
{
return Expr { op1 , "<", op2 };
}
template <typename T>
inline Expr operator <= (const Field<T> &op1, const Field<T> &op2)
{
return Expr { op1, "<=", op2 };
}
// Nullable Field == / != nullptr
template <typename T>
inline Expr operator == (const NullableField<T> &op, std::nullptr_t)
{
return Expr { op, " is null" };
}
template <typename T>
inline Expr operator != (const NullableField<T> &op, std::nullptr_t)
{
return Expr { op, " is not null" };
}
// String Field & / | std::string
inline Expr operator & (const Field<std::string> &field,
std::string val)
{
return Expr (field, " like ", std::move (val));
}
inline Expr operator | (const Field<std::string> &field,
std::string val)
{
return Expr (field, " not like ", std::move (val));
}
// Aggregate Function Helpers
inline auto Count ()
{
return Aggregate<size_t> { "count (*)" };
}
template <typename T>
inline auto Count (const Field<T> &field)
{
return Aggregate<T> { "count", field };
}
template <typename T>
inline auto Sum (const Field<T> &field)
{
return Aggregate<T> { "sum", field };
}
template <typename T>
inline auto Avg (const Field<T> &field)
{
return Aggregate<T> { "avg", field };
}
template <typename T>
inline auto Max (const Field<T> &field)
{
return Aggregate<T> { "max", field };
}
template <typename T>
inline auto Min (const Field<T> &field)
{
return Aggregate<T> { "min", field };
}
}
class ORMapper;
class Constraint
{
protected:
std::string constraint;
std::string field;
Constraint (std::string &&_constraint,
std::string _field = std::string {})
: constraint (_constraint), field (std::move (_field))
{}
friend class ORMapper;
public:
struct CompositeField
{
std::string fieldName;
const std::string *tableName;
template <typename... Fields>
CompositeField (const Fields & ... args)
: tableName (nullptr)
{
(void) BOT_ORM_Impl::Expander
{
0, (Extract (args), 0)...
};
}
private:
template <typename T>
void Extract (const Expression::Field<T> &field)
{
if (tableName != nullptr && tableName != field.tableName)
throw std::runtime_error (NOT_SAME_TABLE);
tableName = field.tableName;
if (fieldName.empty ())
fieldName = field.fieldName;
else
fieldName = field.fieldName + "," + fieldName;
}
};
template <typename T>
static inline Constraint Default (
const Expression::Field<T> &field,
const T &value)
{
std::ostringstream os;
BOT_ORM_Impl::SerializationHelper::
Serialize (os << " default ", value);
return Constraint { os.str (), field.fieldName };
}
static inline Constraint Check (
const Expression::Expr &expr)
{
return Constraint { "check (" + expr.ToString () + ")" };
}
template <typename T>
static inline Constraint Unique (
const Expression::Field<T> &field)
{
return Constraint { "unique (" + field.fieldName + ")" };
}
static inline Constraint Unique (
const CompositeField &fields)
{
return Constraint { "unique (" + fields.fieldName + ")" };
}
template <typename T>
static inline Constraint Reference (
const Expression::Field<T> &field,
const Expression::Field<T> &refered)
{
return Constraint {
std::string ("foreign key (") + field.fieldName +
") references " + *(refered.tableName) +
"(" + refered.fieldName + ")" };
}
static inline Constraint Reference (
const CompositeField &field,
const CompositeField &refered)
{
return Constraint {
std::string ("foreign key (") + field.fieldName +
") references " + *(refered.tableName) +
"(" + refered.fieldName + ")" };
}
};
template <typename T>
class Queryable;
}
namespace BOT_ORM_Impl
{
// Why Remove QueryableHelper from Query?
// Query is a template but the Helper can be shared
class QueryableHelper
{
template <typename T>
using Nullable = BOT_ORM::Nullable<T>;
template <typename T>
using Selectable = BOT_ORM::Expression::Selectable<T>;
protected:
template <typename T>
friend class BOT_ORM::Queryable;
// #1 Tuple Visitor
// Apply 'Fn' to each of element of Tuple
template <typename Fn, typename TupleType, std::size_t... I>
static inline void TupleVisit_Impl (
TupleType &tuple, Fn fn, std::index_sequence<I...>)
{
(void) BOT_ORM_Impl::Expander
{
0, ((void) fn (std::get<I> (tuple)), 0)...
};
}
// Produce the 'index_sequence' for 'tuple'
template <typename Fn, typename TupleType>
static inline void TupleVisit (TupleType &tuple, Fn fn)
{
constexpr auto size = std::tuple_size<TupleType>::value;
return TupleVisit_Impl (tuple, fn,
std::make_index_sequence<size> {});
}
// #2 Join To Tuple
// Type To Nullable
// Get Nullable Type Wrappers for Non-nullable Types
template <typename T> struct TypeToNullable
{
using type = Nullable<T>;
};
template <typename T> struct TypeToNullable <Nullable<T>>
{
using type = Nullable<T>;
};
// Fields To Nullable
// Apply 'TypeToNullable' to each element of Fields
template <typename... Args>
static inline auto FieldsToNullable (Args...)
{
// Unpacking Tricks :-)
// Expand each of 'Args'
// with 'TypeToNullable_t<...>' as a sequence
return std::tuple<
typename TypeToNullable<Args>::type...
> {};
}
// QueryResult To Nullable
template <typename C>
static inline auto QueryResultToTuple (const C &arg)
{
return BOT_ORM_Impl::InjectionHelper::Visit (
arg, [] (auto ... args)
{
return FieldsToNullable (args...);
});
}
template <typename... Args>
static inline auto QueryResultToTuple (
const std::tuple<Args...>& t)
{
// FieldsToNullable is not necessary: Nullable already
return t;
}
// Construct Tuple from QueryResult List
template <typename... Args>
static inline auto JoinToTuple (const Args & ... args)
{
// Unpacking Tricks :-)
return decltype (std::tuple_cat (
QueryResultToTuple (args)...
)) {};
}
// #3 Select To Tuple
// Selectable To Tuple
template <typename T>
static inline auto SelectableToTuple (const Selectable<T> &)
{
// Notes: Only 'const Selectable<T> &' will overload...
return Nullable<T> {};
}
// Construct Tuple from Selectable<T> List
template <typename... Args>
static inline auto SelectToTuple (const Args & ... args)
{
// Unpacking Tricks :-)
return std::tuple <
decltype (SelectableToTuple (args))...
> {};
}
// #4 Field To SQL
// Return Field Strings for GroupBy, OrderBy and Select
template <typename T>
static inline std::string FieldToSql (const Selectable<T> &op)
{
if (op.tableName) return (*op.tableName) + "." + op.fieldName;
else return op.fieldName;
}
template <typename T, typename... Args>
static inline std::string FieldToSql (const Selectable<T> &arg,
const Args & ... args)
{
return FieldToSql (arg) + "," + FieldToSql (args...);
}
};
}
namespace BOT_ORM
{
// Queryable
template <typename QueryResult>
class Queryable
{
template <typename C>
using HasInjected =
BOT_ORM_Impl::InjectionHelper::HasInjected<C>;
protected:
std::shared_ptr<BOT_ORM_Impl::SQLConnector> _connector;
QueryResult _queryHelper;
std::string _sqlFrom;
std::string _sqlSelect;
std::string _sqlTarget;
std::string _sqlWhere;
std::string _sqlGroupBy;
std::string _sqlHaving;
std::string _sqlOrderBy;
std::string _sqlLimit;
std::string _sqlOffset;
Queryable (
std::shared_ptr<BOT_ORM_Impl::SQLConnector> connector,
QueryResult queryHelper,
std::string sqlFrom,
std::string sqlSelect = "select ",
std::string sqlTarget = "*",
std::string sqlWhere = std::string {},
std::string sqlGroupBy = std::string {},
std::string sqlHaving = std::string {},
std::string sqlOrderBy = std::string {},
std::string sqlLimit = std::string {},
std::string sqlOffset = std::string {})
:
_connector (std::move (connector)),
_queryHelper (std::move (queryHelper)),
_sqlFrom (std::move (sqlFrom)),
_sqlSelect (std::move (sqlSelect)),
_sqlTarget (std::move (sqlTarget)),
_sqlWhere (std::move (sqlWhere)),
_sqlGroupBy (std::move (sqlGroupBy)),
_sqlHaving (std::move (sqlHaving)),
_sqlOrderBy (std::move (sqlOrderBy)),
_sqlLimit (std::move (sqlLimit)),
_sqlOffset (std::move (sqlOffset))
{}
template <typename Q> friend class Queryable;
friend class ORMapper;
public:
// Distinct
inline Queryable Distinct () const &
{
auto ret = *this;
ret._sqlSelect = "select distinct ";
return ret;
}
inline Queryable Distinct () &&
{
(*this)._sqlSelect = "select distinct ";
return std::move (*this);
}
// Where
inline Queryable Where (const Expression::Expr &expr) const &
{
auto ret = *this;
ret._sqlWhere = " where (" + expr.ToString () + ")";
return ret;
}
inline Queryable Where (const Expression::Expr &expr) &&
{
(*this)._sqlWhere = " where (" + expr.ToString () + ")";
return std::move (*this);
}
// Group By
template <typename T>
inline Queryable GroupBy (const Expression::Field<T> &field) const &
{
auto ret = *this;
ret._sqlGroupBy = " group by " +
BOT_ORM_Impl::QueryableHelper::FieldToSql (field);
return ret;
}
template <typename T>
inline Queryable GroupBy (const Expression::Field<T> &field) &&
{
(*this)._sqlGroupBy = " group by " +
BOT_ORM_Impl::QueryableHelper::FieldToSql (field);
return std::move (*this);
}
// Having
inline Queryable Having (const Expression::Expr &expr) const &
{
auto ret = *this;
ret._sqlHaving = " having " + expr.ToString ();
return ret;
}
inline Queryable Having (const Expression::Expr &expr) &&
{
(*this)._sqlHaving = " having " + expr.ToString ();
return std::move (*this);
}
// Limit
inline Queryable Take (size_t count) const &
{
auto ret = *this;
ret._sqlLimit = " limit " + std::to_string (count);
return ret;
}
inline Queryable Take (size_t count) &&
{
(*this)._sqlLimit = " limit " + std::to_string (count);
return std::move (*this);
}
// Offset
inline Queryable Skip (size_t count) const &
{
auto ret = *this;
if (ret._sqlLimit.empty ())
ret._sqlLimit = " limit ~0"; // ~0 is a trick :-)
ret._sqlOffset = " offset " + std::to_string (count);
return ret;
}
inline Queryable Skip (size_t count) &&
{
if ((*this)._sqlLimit.empty ())
(*this)._sqlLimit = " limit ~0"; // ~0 is a trick :-)
(*this)._sqlOffset = " offset " + std::to_string (count);
return std::move (*this);
}
// Order By
template <typename T>
inline Queryable OrderBy (
const Expression::Field<T> &field) const &
{
auto ret = *this;
if (ret._sqlOrderBy.empty ())
ret._sqlOrderBy = " order by " +
BOT_ORM_Impl::QueryableHelper::FieldToSql (field);
else
ret._sqlOrderBy += "," +
BOT_ORM_Impl::QueryableHelper::FieldToSql (field);
return ret;
}
template <typename T>
inline Queryable OrderBy (
const Expression::Field<T> &field) &&
{
if ((*this)._sqlOrderBy.empty ())
(*this)._sqlOrderBy = " order by " +
BOT_ORM_Impl::QueryableHelper::FieldToSql (field);
else
(*this)._sqlOrderBy += "," +
BOT_ORM_Impl::QueryableHelper::FieldToSql (field);
return std::move (*this);
}
// Order By Desc
template <typename T>
inline Queryable OrderByDescending (
const Expression::Field<T> &field) const &
{
auto ret = *this;
if (ret._sqlOrderBy.empty ())
ret._sqlOrderBy = " order by " +
BOT_ORM_Impl::QueryableHelper::FieldToSql (field) + " desc";
else
ret._sqlOrderBy += "," +
BOT_ORM_Impl::QueryableHelper::FieldToSql (field) + " desc";
return ret;
}
template <typename T>
inline Queryable OrderByDescending (
const Expression::Field<T> &field) &&
{
if ((*this)._sqlOrderBy.empty ())
(*this)._sqlOrderBy = " order by " +
BOT_ORM_Impl::QueryableHelper::FieldToSql (field) + " desc";
else
(*this)._sqlOrderBy += "," +
BOT_ORM_Impl::QueryableHelper::FieldToSql (field) + " desc";
return std::move (*this);
}
// Select
template <typename... Args>
inline auto Select (const Args & ... args) const
{
return _NewQuery (
BOT_ORM_Impl::QueryableHelper::FieldToSql (args...),
_sqlFrom,
BOT_ORM_Impl::QueryableHelper::SelectToTuple (args...)
);
}
// Join
template <typename C>
inline auto Join (const C &,
const Expression::Expr &,
std::enable_if_t<
!HasInjected<C>::value>
* = nullptr) const
{}
template <typename C>
inline auto Join (const C &queryHelper2,
const Expression::Expr &onExpr,
std::enable_if_t<
HasInjected<C>::value>
* = nullptr) const
{
return _NewJoinQuery (queryHelper2, onExpr, " join ");
}
// Left Join
template <typename C>
inline auto LeftJoin (const C &,
const Expression::Expr &,
std::enable_if_t<
!HasInjected<C>::value>
* = nullptr) const
{}
template <typename C>
inline auto LeftJoin (const C &queryHelper2,
const Expression::Expr &onExpr,
std::enable_if_t<
HasInjected<C>::value>
* = nullptr) const
{
return _NewJoinQuery (queryHelper2, onExpr, " left join ");
}
// Compound Select
inline Queryable Union (const Queryable &queryable) const
{
return _NewCompoundQuery (queryable, " union ");
}
inline Queryable UnionAll (Queryable queryable) const
{
return _NewCompoundQuery (queryable, " union all ");
}
inline Queryable Intersect (Queryable queryable) const
{
return _NewCompoundQuery (queryable, " intersect ");
}
inline Queryable Except (Queryable queryable) const
{
return _NewCompoundQuery (queryable, " excpet ");
}
// Get Result
template <typename T>
Nullable<T> Aggregate (const Expression::Aggregate<T> &agg) const
{
Nullable<T> ret;
_connector->ExecuteCallback (_sqlSelect + agg.fieldName +
_GetFromSql () + _GetLimit () + ";",
[&] (int argc, char **argv)
{
if (argc != 1)
throw std::runtime_error (BAD_COLUMN_COUNT);
BOT_ORM_Impl::DeserializationHelper::
Deserialize (ret, argv[0]);
});
return ret;
}
std::vector<QueryResult> ToVector () const
{
std::vector<QueryResult> ret;
_Select (_queryHelper, ret);
return ret;
}
std::list<QueryResult> ToList () const
{
std::list<QueryResult> ret;
_Select (_queryHelper, ret);
return ret;
}
protected:
// Return FROM part for Query
inline std::string _GetFromSql () const
{
return _sqlFrom + _sqlWhere + _sqlGroupBy + _sqlHaving;
}
// Return ORDER BY & LIMIT part for Query
inline std::string _GetLimit () const
{
return _sqlOrderBy + _sqlLimit + _sqlOffset;
}
// Return a new Queryable Object
template <typename... Args>
inline Queryable<std::tuple<Args...>> _NewQuery (
std::string sqlTarget,
std::string sqlFrom,
std::tuple<Args...> &&newQueryHelper) const
{
return Queryable<std::tuple<Args...>> (
_connector, newQueryHelper,
std::move (sqlFrom),
_sqlSelect, std::move (sqlTarget),
_sqlWhere, _sqlGroupBy, _sqlHaving,
_sqlOrderBy, _sqlLimit, _sqlOffset);
}
// Return a new Join Queryable Object
template <typename C>
inline auto _NewJoinQuery (const C &queryHelper2,
const Expression::Expr &onExpr,
std::string joinStr) const
{
return _NewQuery (
_sqlTarget,
_sqlFrom + std::move (joinStr) +
BOT_ORM_Impl::InjectionHelper::TableName (queryHelper2) +
" on " + onExpr.ToString (),
BOT_ORM_Impl::QueryableHelper::JoinToTuple (
_queryHelper, queryHelper2));
}
// Return a new Compound Queryable Object
Queryable _NewCompoundQuery (const Queryable &queryable,
std::string compoundStr) const
{
auto ret = *this;
ret._sqlFrom = ret._GetFromSql () +
std::move (compoundStr) +
queryable._sqlSelect + queryable._sqlTarget +
queryable._GetFromSql ();
ret._sqlWhere.clear ();
ret._sqlGroupBy.clear ();
ret._sqlHaving.clear ();
return ret;
}
// Select for Normal Objects
template <typename C, typename Out>
inline void _Select (const C &, Out &out) const
{
auto copy = _queryHelper;
_connector->ExecuteCallback (_sqlSelect + _sqlTarget +
_GetFromSql () + _GetLimit () + ";",
[&] (int argc, char **argv)
{
BOT_ORM_Impl::InjectionHelper::Visit (
copy, [argc] (auto & ... args)
{
if (sizeof... (args) != argc)
throw std::runtime_error (BAD_COLUMN_COUNT);
});
BOT_ORM_Impl::InjectionHelper::Visit (
copy, [argv] (auto & ... args)
{
size_t index = 0;
(void) BOT_ORM_Impl::Expander
{
0, (BOT_ORM_Impl::DeserializationHelper::
Deserialize (args, argv[index++]), 0)...
};
});
out.push_back (copy);
});
}
// Select for Tuples
template <typename Out, typename... Args>
inline void _Select (const std::tuple<Args...> &, Out &out) const
{
auto copy = _queryHelper;
_connector->ExecuteCallback (_sqlSelect + _sqlTarget +
_GetFromSql () + _GetLimit () + ";",
[&] (int argc, char **argv)
{
if (sizeof... (Args) != argc)
throw std::runtime_error (BAD_COLUMN_COUNT);
size_t index = 0;
BOT_ORM_Impl::QueryableHelper::TupleVisit (
copy, [argv, &index] (auto &val)
{
BOT_ORM_Impl::DeserializationHelper::
Deserialize (val, argv[index++]);
});
out.push_back (copy);
});
}
};
// ORMapper
class ORMapper
{
template <typename C>
using HasInjected =
BOT_ORM_Impl::InjectionHelper::HasInjected<C>;
public:
ORMapper (const std::string &connectionString)
: _connector (
std::make_shared<BOT_ORM_Impl::SQLConnector> (
connectionString))
{
_connector->Execute ("PRAGMA foreign_keys = ON;");
}
template <typename Fn>
void Transaction (Fn fn)
{
try
{
_connector->Execute ("begin transaction;");
fn ();
_connector->Execute ("commit transaction;");
}
catch (...)
{
_connector->Execute ("rollback transaction;");
throw;
}
}
template <typename C, typename... Args>
std::enable_if_t<!HasInjected<C>::value>
CreateTbl (const C &, const Args & ...)
{}
template <typename C, typename... Args>
std::enable_if_t<HasInjected<C>::value>
CreateTbl (const C &entity,
const Args & ... constraints)
{
const auto &fieldNames =
BOT_ORM_Impl::InjectionHelper::FieldNames (entity);
std::unordered_map<std::string, std::string> fieldFixes;
auto addTypeStr = [&fieldNames, &fieldFixes] (
const auto &arg, size_t index)
{
// Why addTypeStr?
// Walkaround the 'undefined reference' in gcc/clang
constexpr const char *typeStr = BOT_ORM_Impl::TypeString<
std::remove_cv_t<std::remove_reference_t<decltype(arg)>>
>::typeStr;
fieldFixes.emplace (fieldNames[index], typeStr);
};
(void) addTypeStr;
BOT_ORM_Impl::InjectionHelper::Visit (
entity, [&addTypeStr] (const auto & ... args)
{
size_t index = 0;
(void) BOT_ORM_Impl::Expander
{
0, (addTypeStr (args, index++), 0)...
};
});
fieldFixes[fieldNames[0]] += " primary key";
std::string tableFixes;
_GetConstraints (tableFixes, fieldFixes, constraints...);
std::string strFmt;
for (const auto &field : fieldNames)
strFmt += field + fieldFixes[field] + ",";
strFmt += std::move (tableFixes);
strFmt.pop_back ();
_connector->Execute (
"create table " +
BOT_ORM_Impl::InjectionHelper::TableName (entity) +
"(" + strFmt + ");");
}
template <typename C>
std::enable_if_t<!HasInjected<C>::value>
DropTbl (const C &)
{}
template <typename C>
std::enable_if_t<HasInjected<C>::value>
DropTbl (const C &entity)
{
_connector->Execute (
"drop table " +
BOT_ORM_Impl::InjectionHelper::TableName (entity) +
";");
}
template <typename C>
std::enable_if_t<!HasInjected<C>::value>
Insert (const C &, bool = true)
{}
template <typename C>
std::enable_if_t<HasInjected<C>::value>
Insert (const C &entity, bool withId = true)
{
std::ostringstream os;
_GetInsert (os, entity, withId);
_connector->Execute (os.str ());
}
template <typename In, typename C = typename In::value_type>
std::enable_if_t<!HasInjected<C>::value>
InsertRange (const In &, bool = true)
{}
template <typename In, typename C = typename In::value_type>
std::enable_if_t<HasInjected<C>::value>
InsertRange (const In &entities, bool withId = true)
{
std::ostringstream os;
auto anyEntity = false;
for (const auto &entity : entities)
{
_GetInsert (os, entity, withId);
anyEntity = true;
}
if (anyEntity)
_connector->Execute (os.str ());
}
template <typename C>
std::enable_if_t<!HasInjected<C>::value>
Update (const C &)
{}
template <typename C>
std::enable_if_t<HasInjected<C>::value>
Update (const C &entity)
{
std::ostringstream os;
if (_GetUpdate (os, entity))
_connector->Execute (os.str ());
}
template <typename In, typename C = typename In::value_type>
std::enable_if_t<!HasInjected<C>::value>
UpdateRange (const In &)
{}
template <typename In, typename C = typename In::value_type>
std::enable_if_t<HasInjected<C>::value>
UpdateRange (const In &entities)
{
std::ostringstream os, osTmp;
for (const auto &entity : entities)
if (_GetUpdate (osTmp, entity))
{
os << osTmp.str ();
osTmp.str (std::string {}); // Flush the previous data
}
auto sql = os.str ();
if (!sql.empty ()) _connector->Execute (sql);
}
template <typename C>
std::enable_if_t<!HasInjected<C>::value>
Update (const C &,
const Expression::SetExpr &,
const Expression::Expr &)
{}
template <typename C>
std::enable_if_t<HasInjected<C>::value>
Update (const C &entity,
const Expression::SetExpr &setExpr,
const Expression::Expr &whereExpr)
{
_connector->Execute (
"update " +
BOT_ORM_Impl::InjectionHelper::TableName (entity) +
" set " + setExpr.ToString () +
" where " +
whereExpr.ToString () + ";");
}
template <typename C>
std::enable_if_t<!HasInjected<C>::value>
Delete (const C &)
{}
template <typename C>
std::enable_if_t<HasInjected<C>::value>
Delete (const C &entity)
{
const auto &fieldNames =
BOT_ORM_Impl::InjectionHelper::FieldNames (entity);
std::ostringstream os;
os << "delete from "
<< BOT_ORM_Impl::InjectionHelper::TableName (entity)
<< " where " << fieldNames[0] << "=";
// Primary Key
BOT_ORM_Impl::InjectionHelper::Visit (
entity, [&os] (const auto &primaryKey,
const auto & ... dummy)
{
// Why 'eatdummy'?
// Walkaround 'fatal error c1001:
// an internal error has occurred in the compiler.' on MSVC 14
auto eatdummy = [] (const auto &) {};
(void) eatdummy;
// Why 'dummy'?
// Walkaround 'template argument deduction/substitution failed'
// on gcc 5.4
(void) BOT_ORM_Impl::Expander
{
0, (eatdummy (dummy), 0)...
};
if (!BOT_ORM_Impl::SerializationHelper::
Serialize (os, primaryKey))
os << "null";
});
os << ";";
_connector->Execute (os.str ());
}
template <typename C>
std::enable_if_t<!HasInjected<C>::value>
Delete (const C &,
const Expression::Expr &)
{}
template <typename C>
std::enable_if_t<HasInjected<C>::value>
Delete (const C &entity,
const Expression::Expr &whereExpr)
{
_connector->Execute (
"delete from " +
BOT_ORM_Impl::InjectionHelper::TableName (entity) +
" where " +
whereExpr.ToString () + ";");
}
template <typename C>
std::enable_if_t<!HasInjected<C>::value, Queryable<C>>
Query (C)
{}
template <typename C>
std::enable_if_t<HasInjected<C>::value, Queryable<C>>
Query (C queryHelper)
{
return Queryable<C> (
_connector,
std::move (queryHelper),
std::string (" from ") +
BOT_ORM_Impl::InjectionHelper::TableName (queryHelper));
}
protected:
std::shared_ptr<BOT_ORM_Impl::SQLConnector> _connector;
static void _GetConstraints (
std::string &,
std::unordered_map<std::string, std::string> &)
{}
template <typename... Args>
static void _GetConstraints (
std::string &tableFixes,
std::unordered_map<std::string, std::string> &fieldFixes,
const Constraint &constraint,
const Args & ... args)
{
if (!constraint.field.empty ())
fieldFixes[constraint.field] += constraint.constraint;
else
tableFixes += constraint.constraint + ",";
_GetConstraints (tableFixes, fieldFixes, args...);
}
template <typename C>
static inline void _GetInsert (
std::ostream &os, const C &entity, bool withId)
{
BOT_ORM_Impl::InjectionHelper::Visit (
entity, [&os, &entity, withId] (
const auto &primaryKey, const auto & ... args)
{
const auto &fieldNames =
BOT_ORM_Impl::InjectionHelper::FieldNames (entity);
os << "insert into "
<< BOT_ORM_Impl::InjectionHelper::TableName (entity)
<< "(";
std::ostringstream osVal;
// Avoid Bad Eating of ,
bool anyField = false;
auto serializeField =
[&fieldNames, &os, &osVal, &anyField] (
const auto &val, size_t index)
{
if (BOT_ORM_Impl::SerializationHelper::
Serialize (osVal, val))
{
os << fieldNames[index] << ",";
osVal << ",";
anyField = true;
}
};
// Priamry Key
if (withId &&
BOT_ORM_Impl::SerializationHelper::
Serialize (osVal, primaryKey))
{
os << fieldNames[0] << ",";
osVal << ",";
anyField = true;
}
// The Rest
size_t index = 1;
(void) BOT_ORM_Impl::Expander
{
0, (serializeField (args, index++), 0)...
};
(void) serializeField;
if (anyField)
{
os.seekp (os.tellp () - std::streamoff (1));
osVal.seekp (osVal.tellp () - std::streamoff (1));
}
else // Fix for No Field for Insert...
{
os << fieldNames[0];
osVal << "null";
}
osVal << ");"; // To Enable Seekp...
os << ") values (" << osVal.str ();
});
}
template <typename C>
static inline bool _GetUpdate (
std::ostream &os, const C &entity)
{
return BOT_ORM_Impl::InjectionHelper::Visit (
entity, [&os, &entity] (
const auto &primaryKey, const auto & ... args)
{
if (sizeof... (args) == 0)
return false;
const auto &fieldNames =
BOT_ORM_Impl::InjectionHelper::FieldNames (entity);
os << "update "
<< BOT_ORM_Impl::InjectionHelper::TableName (entity)
<< " set ";
auto serializeField = [&fieldNames, &os] (
const auto &val, size_t index)
{
os << fieldNames[index] << "=";
if (!BOT_ORM_Impl::SerializationHelper::
Serialize (os, val))
os << "null";
os << ",";
};
// The Rest
size_t index = 1;
(void) BOT_ORM_Impl::Expander
{
0, (serializeField (args, index++), 0)...
};
(void) serializeField;
os.seekp (os.tellp () - std::streamoff (1));
// Primary Key
os << " where " << fieldNames[0] << "=";
if (!BOT_ORM_Impl::SerializationHelper::
Serialize (os, primaryKey))
os << "null";
os << ";";
return true;
});
}
};
// Field Extractor
class FieldExtractor
{
using pair_type = std::pair<
const std::string &,
const std::string &>;
template <typename C>
using HasInjected =
BOT_ORM_Impl::InjectionHelper::HasInjected<C>;
template <typename C>
std::enable_if_t<!HasInjected<C>::value>
Extract (const C &)
{}
template <typename C>
std::enable_if_t<HasInjected<C>::value>
Extract (const C &helper)
{
BOT_ORM_Impl::InjectionHelper::Visit (
helper, [this, &helper] (
const auto & ... args)
{
const auto &fieldNames =
BOT_ORM_Impl::InjectionHelper::FieldNames (helper);
const auto &tableName =
BOT_ORM_Impl::InjectionHelper::TableName (helper);
size_t index = 0;
(void) BOT_ORM_Impl::Expander
{
0, (_map.emplace (
(const void *) &args,
pair_type { fieldNames[index++], tableName }
), 0)...
};
});
}
public:
template <typename... Classes>
FieldExtractor (const Classes & ... args)
{
(void) BOT_ORM_Impl::Expander
{
0, (Extract (args), 0)...
};
}
template <typename T>
inline Expression::Field<T> operator () (
const T &field) const
{
const auto &result = Get (field);
return Expression::Field<T> {
std::move (result.first), &result.second };
}
template <typename T>
inline Expression::NullableField<T> operator () (
const Nullable<T> &field) const
{
const auto &result = Get (field);
return Expression::NullableField<T> {
std::move (result.first), &result.second };
}
private:
std::unordered_map<const void *, pair_type> _map;
template <typename T>
const pair_type &Get (const T &field) const
{
try { return _map.at ((const void *) &field); }
catch (...) { throw std::runtime_error (NO_FIELD); }
}
};
}
// Clear Intra Macros
#undef NO_ORMAP
#undef BAD_TYPE
#undef NO_FIELD
#undef BAD_COLUMN_COUNT
#undef NULL_DESERIALIZE
#undef NOT_SAME_TABLE
#endif // !BOT_ORM_H
================================================
FILE: test/makefile
================================================
TARGET = test
SOURCES = test.cpp ../src/sqlite3.c
WARNINGFLAGS = -Wall -W
CPPFLAGS = -std=c++14
LINKS = -lstdc++ -lpthread -ldl
OBJS = $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES)))
%.o: %.c
$(CC) -c $< -o $@ $(WARNINGFLAGS) $(LINKS)
%.o: %.cpp
$(CC) -c $< -o $@ $(WARNINGFLAGS) $(LINKS) $(CPPFLAGS)
$(TARGET): $(OBJS)
$(CC) $(OBJS) -o $(TARGET) $(LINKS)
clean:
rm -rf $(OBJS) $(TARGET) *.db
================================================
FILE: test/test.cpp
================================================
// Test of ORM Lite
// https://github.com/BOT-Man-JL/ORM-Lite
// BOT Man, 2016
#include <iostream>
#include <memory>
#include <string>
#include "../src/ormlite.h"
using namespace BOT_ORM;
using namespace BOT_ORM::Expression;
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#define TESTDB "test.db"
struct ModelA
{
int a_int;
std::string a_string;
double a_double;
Nullable<int> an_int;
Nullable<double> an_double;
Nullable<std::string> an_string;
// Inject ORM-Lite into this Class :-)
ORMAP ("ModelA", a_int, a_string, a_double, an_int, an_double, an_string);
};
struct ModelB
{
unsigned long b_ulong;
float b_float;
Nullable<unsigned long> bn_ulong;
Nullable<float> bn_float;
// Inject ORM-Lite into this Class :-)
ORMAP ("ModelB", b_ulong, b_float, bn_ulong, bn_float);
};
struct ModelC
{
unsigned c_uint;
int a_int;
unsigned long b_ulong;
// Inject ORM-Lite into this Class :-)
ORMAP ("ModelC", c_uint, a_int, b_ulong);
};
struct ModelD
{
int d_int;
// Inject ORM-Lite into this Class :-)
ORMAP ("ModelD", d_int);
};
namespace detail
{
template<typename Model>
void ResetTable (const Model &model)
{
ORMapper mapper (TESTDB);
try {
mapper.CreateTbl (model);
}
catch (...) {
mapper.DropTbl (model);
mapper.CreateTbl (model);
}
}
}
void ResetTables () {}
template<typename Model, typename ...OtherModels>
void ResetTables (const Model &model, const OtherModels &...models)
{
detail::ResetTable (model);
ResetTables (models...);
}
TEST_CASE ("create/drop tables")
{
ResetTables (ModelA {}, ModelB {}, ModelC {}, ModelD {});
ORMapper mapper (TESTDB);
mapper.DropTbl (ModelA {});
mapper.DropTbl (ModelB {});
mapper.DropTbl (ModelC {});
mapper.DropTbl (ModelD {});
mapper.CreateTbl (ModelA {});
mapper.CreateTbl (ModelB {});
mapper.CreateTbl (ModelC {});
mapper.CreateTbl (ModelD {});
}
TEST_CASE ("normal cases")
{
ModelA ma;
ModelD md;
auto field = FieldExtractor { ma, md };
ORMapper mapper (TESTDB);
mapper.Insert (ModelD { 0 });
mapper.Insert (ModelD { 0 }, false);
mapper.InsertRange (std::list<ModelD> { ModelD { 2 }, ModelD { 3 } });
mapper.InsertRange (std::list<ModelD> { ModelD { 2 }, ModelD { 3 } }, false);
mapper.Update (ModelD { 0 });
mapper.UpdateRange (std::list<ModelD> { ModelD { 2 }, ModelD { 3 } });
mapper.Update (ModelD {}, field (md.d_int) = 6, field (md.d_int) == 0); // 0 -> 6
mapper.Delete (ModelD { 1 });
mapper.Delete (ModelD {}, field (md.d_int) == 0); // No such one
constexpr auto countExpected = 5;
constexpr auto firstIdExpected = 2;
constexpr auto lastIdExpected = 6;
// Expected: 2, 3, 4, 5, 6
REQUIRE (mapper.Query (ModelD {})
.Aggregate (Count ()).Value () == countExpected);
REQUIRE (mapper.Query (ModelD {})
.LeftJoin (ModelA {}, field (ma.a_int) == field (md.d_int))
.Aggregate (Count ()).Value () == countExpected);
mapper.Insert (ModelA {}, false);
REQUIRE (mapper.Query (ModelD {}).Select (field (md.d_int))
.Union (mapper.Query (ModelA {}).Select (field (ma.a_int)))
.ToList ().size () == countExpected + 1);
REQUIRE (mapper.Query (ModelD {})
.ToVector ()[countExpected - 1].d_int == lastIdExpected);
auto firstTuple = mapper.Query (ModelD {})
.Select (field (md.d_int))
.ToList ().front ();
REQUIRE (std::get<0> (firstTuple).Value () == firstIdExpected);
}
TEST_CASE ("handle existing table")
{
// before
ResetTables (ModelD {});
{
sqlite3 *db;
sqlite3_open (TESTDB, &db);
sqlite3_exec (db,
"DROP TABLE ModelD;"
"CREATE TABLE ModelD (d_int INTEGER, d_str TEXT);"
"INSERT INTO ModelD values (1, 'John');",
nullptr, nullptr, nullptr);
sqlite3_close (db);
}
// test
ORMapper mapper (TESTDB);
REQUIRE_THROWS_WITH (mapper.Query (ModelD {}).ToList (),
"SQL error: 'Bad Column Count' at 'select * from ModelD;'");
}
TEST_CASE ("chinese characters")
{
// before
ResetTables (ModelA {});
// test
ORMapper mapper (TESTDB);
mapper.Insert (
ModelA { 0, "你好", 0, nullptr, nullptr, nullptr }, false);
mapper.Insert (
ModelA { 0, u8"世界", 0, nullptr, nullptr, nullptr }, false);
auto chinese = mapper.Query (ModelA {}).ToVector ();
REQUIRE (chinese.size () == 2);
REQUIRE (chinese[0].a_string == "你好");
REQUIRE (chinese[1].a_string == u8"世界");
}
TEST_CASE ("lifetime of mapper")
{
// before
ResetTables (ModelA {});
{
ORMapper mapper (TESTDB);
mapper.Insert (ModelA {}, false);
mapper.Insert (ModelA {}, false);
}
// test
std::unique_ptr<Queryable<ModelA>> queryable;
{
ORMapper mapper (TESTDB);
queryable.reset (new Queryable<ModelA> { mapper.Query (ModelA {}) });
}
REQUIRE (queryable->ToList ().size () == 2);
}
using InvalidModel = int;
TEST_CASE ("invalid model", "[not-compile]")
{
ORMapper mapper (TESTDB);
//mapper.CreateTbl (InvalidModel {});
//mapper.CreateTbl (InvalidModel {}, Constraint::Unique (Field<int> {"", nullptr}));
//mapper.DropTbl (InvalidModel {});
//mapper.Insert (InvalidModel {});
//mapper.Insert (InvalidModel {}, false);
////mapper.InsertRange (InvalidModel {});
//mapper.InsertRange (std::vector<int> {});
//mapper.InsertRange (std::vector<int> {}, false);
//mapper.Update (InvalidModel {});
////mapper.UpdateRange (InvalidModel {});
//mapper.UpdateRange (std::vector<int> {});
//mapper.Update (InvalidModel {}, SetExpr { "" }, Expr { Selectable<int> {"", nullptr}, "" });
//mapper.Delete (InvalidModel {});
//mapper.Delete (InvalidModel {}, Expr { Selectable<int> {"", nullptr}, "" });
//mapper.Query (InvalidModel {});
//FieldExtractor { InvalidModel {}, double () };
////mapper.Query (ModelA {})
//// .Join (InvalidModel {}, Expr { Selectable<int> {"", nullptr}, "" });
////mapper.Query (ModelA {})
//// .LeftJoin (InvalidModel {}, Expr { Selectable<int> {"", nullptr}, "" });
}
struct SickModel
{
ModelD dd;
char ch;
ORMAP ("SickModel", dd, ch);
};
TEST_CASE ("invalid field", "[not-compile]")
{
ORMapper mapper (TESTDB);
//mapper.CreateTbl (SickModel {});
//mapper.Insert (SickModel {});
//mapper.Update (SickModel {});
//mapper.Delete (SickModel {});
//mapper.Query (SickModel {}).ToList ();
}
================================================
FILE: test/test.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
1781A74F1F5FED13008E6067 /* test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1781A74E1F5FED13008E6067 /* test.cpp */; };
1781A7531F5FEDB3008E6067 /* sqlite3.c in Sources */ = {isa = PBXBuildFile; fileRef = 1781A7511F5FEDB3008E6067 /* sqlite3.c */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
1781A7421F5FECD1008E6067 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1781A7441F5FECD1008E6067 /* test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = test; sourceTree = BUILT_PRODUCTS_DIR; };
1781A74E1F5FED13008E6067 /* test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test.cpp; sourceTree = "<group>"; };
1781A7541F5FEE5D008E6067 /* nullable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nullable.h; path = ../src/nullable.h; sourceTree = "<group>"; };
1781A7501F5FED8A008E6067 /* ormlite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ormlite.h; path = ../src/ormlite.h; sourceTree = "<group>"; };
1781A7511F5FEDB3008E6067 /* sqlite3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sqlite3.c; path = ../src/sqlite3.c; sourceTree = "<group>"; };
1781A7521F5FEDB3008E6067 /* sqlite3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sqlite3.h; path = ../src/sqlite3.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
1781A7411F5FECD1008E6067 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
1781A73B1F5FECD1008E6067 = {
isa = PBXGroup;
children = (
1781A7541F5FEE5D008E6067 /* nullable.h */,
1781A7501F5FED8A008E6067 /* ormlite.h */,
1781A7511F5FEDB3008E6067 /* sqlite3.c */,
1781A7521F5FEDB3008E6067 /* sqlite3.h */,
1781A74E1F5FED13008E6067 /* test.cpp */,
1781A7451F5FECD1008E6067 /* Products */,
);
sourceTree = "<group>";
};
1781A7451F5FECD1008E6067 /* Products */ = {
isa = PBXGroup;
children = (
1781A7441F5FECD1008E6067 /* test */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
1781A7431F5FECD1008E6067 /* test */ = {
isa = PBXNativeTarget;
buildConfigurationList = 1781A74B1F5FECD1008E6067 /* Build configuration list for PBXNativeTarget "test" */;
buildPhases = (
1781A7401F5FECD1008E6067 /* Sources */,
1781A7411F5FECD1008E6067 /* Frameworks */,
1781A7421F5FECD1008E6067 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = test;
productName = test;
productReference = 1781A7441F5FECD1008E6067 /* test */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
1781A73C1F5FECD1008E6067 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0830;
TargetAttributes = {
1781A7431F5FECD1008E6067 = {
CreatedOnToolsVersion = 8.3.3;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 1781A73F1F5FECD1008E6067 /* Build configuration list for PBXProject "test" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 1781A73B1F5FECD1008E6067;
productRefGroup = 1781A7451F5FECD1008E6067 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
1781A7431F5FECD1008E6067 /* test */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
1781A7401F5FECD1008E6067 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1781A74F1F5FED13008E6067 /* test.cpp in Sources */,
1781A7531F5FEDB3008E6067 /* sqlite3.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
1781A7491F5FECD1008E6067 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.12;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
1781A74A1F5FECD1008E6067 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.12;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
};
name = Release;
};
1781A74C1F5FECD1008E6067 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
1781A74D1F5FECD1008E6067 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
1781A73F1F5FECD1008E6067 /* Build configuration list for PBXProject "test" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1781A7491F5FECD1008E6067 /* Debug */,
1781A74A1F5FECD1008E6067 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
1781A74B1F5FECD1008E6067 /* Build configuration list for PBXNativeTarget "test" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1781A74C1F5FECD1008E6067 /* Debug */,
1781A74D1F5FECD1008E6067 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 1781A73C1F5FECD1008E6067 /* Project object */;
}
================================================
FILE: test/test.vcxproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{AD41C840-0ADD-43E1-BF17-60EC79CEEEFC}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>test</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\test\</OutDir>
<IntDir>$(Configuration)\test\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(Platform)\$(Configuration)\test\</IntDir>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\test\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\sqlite3.c" />
<ClCompile Include="test.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\ormlite.h" />
<ClInclude Include="..\src\nullable.h" />
<ClInclude Include="..\src\sqlite3.h" />
<ClInclude Include="catch.hpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
gitextract_a6u7d0as/
├── .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
└── test/
├── makefile
├── test.cpp
├── test.pbxproj
└── test.vcxproj
SYMBOL INDEX (38 symbols across 4 files)
FILE: sample/sample.cpp
type PrintHelper (line 14) | namespace PrintHelper
function PrintNullable (line 17) | void PrintNullable (const BOT_ORM::Nullable<T> &val)
type TuplePrinter (line 26) | struct TuplePrinter
method print (line 28) | static void print (const Tuple& t)
type TuplePrinter<Tuple, 1> (line 37) | struct TuplePrinter<Tuple, 1>
method print (line 39) | static void print (const Tuple& t)
function PrintTuple (line 46) | void PrintTuple (const std::tuple<Args...>& t)
function PrintTuples (line 54) | void PrintTuples (const Container &vals)
type UserModel (line 62) | struct UserModel
type SellerModel (line 76) | struct SellerModel
type OrderModel (line 86) | struct OrderModel
function main (line 98) | int main ()
FILE: src/nullable.h
function namespace (line 17) | namespace BOT_ORM
FILE: src/ormlite.h
function namespace (line 66) | namespace BOT_ORM_Impl
type DeserializationHelper (line 251) | struct DeserializationHelper
function Visit (line 333) | inline decltype (auto) Visit (C &obj, Fn fn)
function std (line 340) | inline const std::vector<std::string> &FieldNames (const C &)
function std (line 348) | inline const std::string &TableName (const C &)
function namespace (line 369) | namespace Expression
type Expr (line 465) | struct Expr
function Expr (line 622) | inline Expr operator & (const Field<std::string> &field,
function Expr (line 628) | inline Expr operator | (const Field<std::string> &field,
function class (line 674) | class Constraint
function Constraint (line 718) | inline Constraint Default (
function class (line 1312) | class ORMapper
function _GetConstraints (line 1571) | static void _GetConstraints (
function _GetConstraints (line 1577) | void _GetConstraints (
function _GetInsert (line 1591) | inline void _GetInsert (
function _GetUpdate (line 1659) | inline bool _GetUpdate (
FILE: test/test.cpp
type ModelA (line 19) | struct ModelA
type ModelB (line 33) | struct ModelB
type ModelC (line 45) | struct ModelC
type ModelD (line 55) | struct ModelD
type detail (line 63) | namespace detail
function ResetTable (line 66) | void ResetTable (const Model &model)
function ResetTables (line 80) | void ResetTables () {}
function ResetTables (line 83) | void ResetTables (const Model &model, const OtherModels &...models)
type SickModel (line 237) | struct SickModel
Condensed preview — 17 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (163K chars).
[
{
"path": ".gitignore",
"chars": 4227,
"preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User"
},
{
"path": ".travis.yml",
"chars": 561,
"preview": "language: cpp\nsudo: required\ncompiler:\n - gcc\n - clang\nscript:\n - unzip -qu src/sqlite3.h.zip -d src\n - unzip -qu sr"
},
{
"path": "LICENSE",
"chars": 1077,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2016 BOT Man JL\n\nPermission is hereby granted, free of charge, to any person obtain"
},
{
"path": "README.md",
"chars": 1508,
"preview": "# ORM Lite\r\n\r\n**ORM Lite** is a C++ [_**Object Relation Mapping** (ORM)_](https://en.wikipedia.org/wiki/Object-relation"
},
{
"path": "appveyor.yml",
"chars": 1333,
"preview": "version: 1.0.{build}\r\nimage: Visual Studio 2017\r\nbuild_script:\r\n - ps: Expand-Archive src/sqlite3.h.zip -DestinationPat"
},
{
"path": "docs/get-started.md",
"chars": 12743,
"preview": "# Get Started\r\n\r\nHere is a **short tour** for this **Amazing** ORM 😉\r\n\r\nThe full code is in [sample.cpp](../sample/samp"
},
{
"path": "docs/orm-lite.md",
"chars": 18916,
"preview": "# ORM Lite\r\n\r\n## Requirements\r\n\r\n- **C++ 14** Support\r\n - MSVC >= 14 (VS 2015 Update 3)\r\n - gcc >= 5.4\r\n - Clang >= "
},
{
"path": "sample/makefile",
"chars": 412,
"preview": "\nTARGET = sample\nSOURCES = sample.cpp ../src/sqlite3.c\nWARNINGFLAGS = -Wall -W\nCPPFLAGS = -std=c++14\nLINKS = -lstdc++ -l"
},
{
"path": "sample/sample.cpp",
"chars": 13343,
"preview": "\n// Sample of ORM Lite\n// https://github.com/BOT-Man-JL/ORM-Lite\n// BOT Man, 2016\n\n#include <string>\n#include <iostream>"
},
{
"path": "sample/sample.pbxproj",
"chars": 9051,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "sample/sample.vcxproj",
"chars": 7549,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.micros"
},
{
"path": "src/nullable.h",
"chars": 2883,
"preview": "\n// Nullable module for ORM Lite\n// ORM Lite - An ORM for SQLite in C++ 17\n// https://github.com/BOT-Man-JL/ORM-Lite\n// "
},
{
"path": "src/ormlite.h",
"chars": 55775,
"preview": "\n// ORM Lite\n// An ORM for SQLite in C++ 14\n// https://github.com/BOT-Man-JL/ORM-Lite\n// BOT Man, 2017\n\n#ifndef BOT_ORM_"
},
{
"path": "test/makefile",
"chars": 408,
"preview": "\nTARGET = test\nSOURCES = test.cpp ../src/sqlite3.c\nWARNINGFLAGS = -Wall -W\nCPPFLAGS = -std=c++14\nLINKS = -lstdc++ -lpthr"
},
{
"path": "test/test.cpp",
"chars": 6662,
"preview": "\n// Test of ORM Lite\n// https://github.com/BOT-Man-JL/ORM-Lite\n// BOT Man, 2016\n\n#include <iostream>\n#include <memory>\n"
},
{
"path": "test/test.pbxproj",
"chars": 8996,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "test/test.vcxproj",
"chars": 7498,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.micros"
}
]
About this extraction
This page contains the full source code of the BOT-Man-JL/ORM-Lite GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 17 files (149.4 KB), approximately 39.3k tokens, and a symbol index with 38 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.