Skip to content

Commit 2744b14

Browse files
The-EDevLeon0402
authored andcommitted
Created docs directory (rbock#364)
Copied wiki into docs directory This allows developers to open pull requests to edit documentation and also use mkdocs to create a styled HTML version * changed wiki links to relative links * removed Planned-Features.md * removed reference to planned features in Home.md
1 parent 88210ef commit 2744b14

13 files changed

+841
-0
lines changed

docs/Database.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#Introduction
2+
sqlpp11 is a library mainly for constructing queries and interpreting results. It does not know how to talk to a database on its own. It needs database connectors for that. Depending on the database you want to use, you can use an existing connector, for instance
3+
4+
* MySQL: https://github.com/rbock/sqlpp11-connector-mysql
5+
* sqlite3: https://github.com/rbock/sqlpp11-connector-sqlite3
6+
* PostgreSQL: https://github.com/matthijs/sqlpp11-connector-postgresql
7+
* STL Container (highly experimental): https://github.com/rbock/sqlpp11-connector-stl
8+
9+
Or you have to write your own connector. Don't worry, it is not that hard.
10+
11+
The api is documented [here](https://github.com/rbock/sqlpp11/blob/master/connector_api/connection.h).

docs/Dynamic-Select.md

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Introduction
2+
_This page explains dynamic select statements. Before studying this page, you should read about [static select statements](Select.md)._
3+
4+
If you know the exact structure of your queries at compile time, statically constructed select statements are perfect. But if the structure depends on runtime information like user input, you will need dynamic select statements. Depending on your needs, you can choose the required dosage.
5+
6+
# A Basic Example
7+
So let us construct select query with a dynamic part
8+
```C++
9+
auto s = dynamic_select(db, all_of(foo)).from(foo).dynamic_where();
10+
if (runtimeQuery.id)
11+
s.where.add(foo.id == runtimeQuery.id);
12+
if (!runtimeQuery.name.empty())
13+
s.where.add(foo.name == runtimeQuery.name);
14+
```
15+
Admittedly, a rather lame example (please suggest nicer ones), but anyway, this is what's happening:
16+
```C++
17+
dynamic_select(db, ...)
18+
```
19+
This initializes a dynamic select. One major difference between `dynamic_select` and `select` is the first argument of `dynamic_select`: a database connection. This is used to evaluate the dynamic parts of the query as they are added.
20+
21+
```C++
22+
.dynamic_where();
23+
...
24+
s.where.add(foo.name == runtimeQuery.name);
25+
s.where.add(foo.id == runtimeQuery.id);
26+
```
27+
The first part creates a new select object that accepts a dynamically constructed where expression. In this case the user can determine whether to search for a certain name or a certain id, or neither or both.
28+
29+
# Dynamic Select
30+
## Dynamic Columns
31+
If the (some) selected columns are not known at compile time, you can do something like this:
32+
```C++
33+
auto s = dynamic_select(db).dynamic_columns(foo.id).from(foo).unconditionally();
34+
if (someCondition)
35+
s.selected_columns.add(foo.name);
36+
if (someOtherCondition)
37+
s.selected_columns.add(foo.hasFun);
38+
```
39+
In this example, the column id is always selected. The other two columns may or may not be selected. This is determined at runtime. This impacts the way the results are accessed because the type of the result row is not known at the same level of detail as in the static case. The dynamic fields can be accessed via name lookup like in a map:
40+
```C++
41+
for (const auto& row : db(s))
42+
{
43+
long id = row.id;
44+
if (someCondition)
45+
std::string name = row.at("name");
46+
if (someOtherCondition)
47+
std::string hasFun = row.at("hasFun");
48+
}
49+
```
50+
This also shows another difference. Dynamic fields are of text type. It would be possible to add conversion methods for other types as well, but this has not been coded yet. Please let me know you wishes.
51+
52+
## Dynamic From
53+
In a static query the compiler can verify whether the `from()` clause is sufficient to support all other aspects of the query.
54+
55+
With a dynamic from, the compile cannot know tables that going into the `from()`. Such checks would therefore be impossible. But it allows you to add joins at runtime!
56+
57+
```C++
58+
auto s = dynamic_select(db, all_of(foo)).dynamic_from(foo).dynamic_where();
59+
if (someOtherCondition)
60+
s.from.add(dynamic_join(bar).on(foo.barId == bar.id));
61+
```
62+
63+
In this example, the user may want to include `bar` into the query.
64+
65+
## Dynamic Where
66+
As shown in other examples, the where condition can be constructed dynamically using the methods `dynamic_where()` and `where.add()`. The former prepares the select to accept dynamic where conditions, the latter adds a condition. Several calls to add_where will be combined into one condition by `and`.
67+
68+
## Dynamic Having, Group By, OrderBy, Limit and Offset
69+
Similar to the dynamic where, you can use `dynamic_having`, `dynamic_group_by`, `dynamic_order_by`, `dynamic_limit` and `dynamic_offset` to prepare the select statement to accept certain dynamic aspects. Using `having.add`, `group_by.add`, `order_by.add`, `limit.set` and `offset.set` you can then adjust those query parts according to the runtime information.

docs/Exception-Handling.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
## When to expect an exception
2+
3+
sqlpp11 connectors throw the majority of `sqlpp::exception`s so check your connector's documentation. Generally, you should expect an exception when:
4+
5+
- Connecting to a database
6+
- Preparing a statement
7+
- Executing a statement
8+
- Retrieving and iterating through result rows
9+
10+
Additionally, the date library used by sqlpp11 may throw `std::runtime_error`. As of 2017-04-08 this only happens when formatting a date using a format string.

docs/Functions.md

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
sqlpp11 offers equivalents for quite a few SQL functions. It also has some additional functions to make the developer's life easier, see [Misc Functions](#Misc-Functions).
2+
3+
If functions can be used as select columns, the column's name is the name of the function. You can use `.as()` of course to give it another name, see [Select](Select.md). There are just a few exceptions like `any()` which cannot be named.
4+
5+
# Member Functions
6+
## in and not_in
7+
The methods `in()` and `not_in()` can be used are available for pretty much any expression. They take zero or more value expressions. You can make the list dynamic by using a container of values.
8+
9+
```C++
10+
tab.a.in(); // not valid SQL but certainly useful for generic code, evaluates to a false expression (version 0.35 and later).
11+
tab.a.in(x, ...); // x, ... being one or more value expressions
12+
tab.a.in(sub_select); // sub_select being a select expression with one result column of appropriate type.
13+
tab.a.in(sqlpp::value_list(some_container_of_values)); // evaluates to a false expression in case of an empty container
14+
```
15+
16+
## is_null
17+
## like
18+
19+
# Aggregate Functions
20+
The following aggregate functions are supported
21+
* avg
22+
* count
23+
* max
24+
* min
25+
* sum
26+
27+
For example:
28+
```C++
29+
for (const auto& row : db(select(tab.name, avg(tab.value))
30+
.from(tab)
31+
.where(tab.id > 17)
32+
.group_by(tab.name)))
33+
{
34+
std::cerr << row.name << ": " << row.avg << std::endl;
35+
}
36+
```
37+
38+
# Text Functions
39+
## concat
40+
Just use the + operator :-)
41+
42+
# Sub-Query Functions
43+
## exists
44+
## any
45+
## some
46+
47+
# Misc Functions
48+
sqlpp11 offers a few functions that do not mimic SQL but are there to make your life easier.
49+
50+
## value
51+
The `value` function turns the argument into an unnamed SQL expression, e.g. an `int` into an `sqlpp::integral` or a `std::string` into an `sqlpp::text`. You probably don't need to use this function too often, because in most cases, sqlpp expressions do the conversion themselves. For instance
52+
```C++
53+
tab.foo + 17
54+
```
55+
is a perfectly valid expression if `tab.foo` represents an SQL integral value. But when doing some generic query programming you might get into the situation that you want to select a constant value. For instance:
56+
```C++
57+
for (const auto& row : select(sqlpp::value<sqlpp::bigint>(7).as(sql::alias::a)).from(tab)))
58+
{
59+
int64_t a = row.a;
60+
}
61+
```
62+
63+
##value_list
64+
`value_list` is a helper function that takes a container of values and turns it into an sqlpp11 construct that is understood by the `in()` member function of expressions, see above.
65+
66+
## verbatim
67+
sqlpp11 supports quite a few aspects of SQL. But it does certainly not cover everything, nor will it ever. So what if you need to use something that is not supported? At least for expressions there is an easy way to use unsupported features, the `verbatim()` method. It requires a template parameter to determine the SQL type and a string argument containing the expression, for instance:
68+
```C++
69+
select(all_of(tab)).from(tab).where(tab.foo == 42 and sqlpp::verbatim<sqlpp::boolean>("mighty special feature"));
70+
```
71+
_Use with care_, as sqlpp11 does not bother to look into the string you provide. That means you have to handle type checking, syntax checking, escaping of injected evil data from your users, etc.
72+
73+
## flatten
74+
Say `tab.foo` and `tab.bar` represent two bigint columns. Then the type of `tab.foo` and `tab.bar` is different. Also `tab.foo + 17` and `tab.bar + 17` are expressions of different type. This is because the expression carries information about required tables for instance (and that is going to be used to check if `from()` contains all the required tables.
75+
The expression templates can get in your way though, if you want to create parts of your query dynamically:
76+
```C++
77+
auto e = (tab.foo == 17);
78+
if (userWantsBar)
79+
e = (tab.bar == 17); // won't compile
80+
```
81+
You can use [dynamic select](Dynamic-Select.md), but there is an alternative, the `flatten()` method, which turns the expression into a more generic expression type (it just "remembers" whether it is a text, boolean etc).
82+
```C++
83+
auto e = flatten(tab.foo == 17);
84+
if (userWantsBar)
85+
e = flatten(tab.bar == 17); // will compile
86+
```

docs/Home.md

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Introduction
2+
Lets see:
3+
* You know C++?
4+
* You know some SQL?
5+
* You want to use SQL in your C++ program?
6+
* You think C++ and SQL should play well together?
7+
* You know which tables you want to use in a database?
8+
* You can cope with a few template error messages in case something is wrong?
9+
10+
You have come to the right place!
11+
12+
sqlpp11 offers you to code SQL in C++ almost naturally. You can use tables, columns and functions. Everything has strong types which allow the compiler to help you a lot. At compile time, it will tell about most of those pesky oversight errors you can make (typos, comparing apples with oranges, forgetting tables in a select statement, etc). And it does not stop at query construction. Results have ranges, and strongly typed members, so that you can browse through results in a type-safe manner, worthy of modern C++.
13+
14+
The following pages will tell you how to use it:
15+
* [Database](Database.md)
16+
* [Tables](Tables.md)
17+
* [Insert](Insert.md)
18+
* [Select](Select.md) <- You might want to read this first as an appetizer
19+
* [Update](Update.md)
20+
* [Remove](Remove.md)
21+
* [Functions](Functions.md)
22+
* [Prepared Statements](Prepared-Statements.md)
23+
* [NULL](NULL.md)
24+
* [New Features](New-Features.md)

docs/Insert.md

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Haven't found the time to document this in any detail, yet, but this is an example:
2+
3+
```C++
4+
db(insert_into(tab).set(tab.gamma = true));
5+
```
6+
7+
This is how you could insert multiple rows at a time:
8+
9+
```C++
10+
auto multi_insert = insert_into(t).columns(t.gamma, t.beta, t.delta);
11+
multi_insert.values.add(t.gamma = true, t.beta = "cheesecake", t.delta = 1);
12+
multi_insert.values.add(t.gamma = sqlpp::default_value, t.beta = sqlpp::default_value,
13+
t.delta = sqlpp::default_value);
14+
multi_insert.values.add(t.gamma = sqlpp::value_or_null(true),
15+
t.beta = sqlpp::value_or_null("pie"),
16+
t.delta = sqlpp::value_or_null<sqlpp::integer>(sqlpp::null));
17+
db(multi_insert);
18+
```

docs/NULL.md

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Introduction
2+
Database NULL is a strange beast. It can be compared to anything but that comparison never returns true. It also never returns false, it returns NULL. Even
3+
4+
```SQL
5+
NULL != NULL -> NULL
6+
NULL = NULL -> NULL
7+
```
8+
9+
A value like that would be pretty unusual in C++. Especially since fields in a result could be either a decent value or NULL. And this can change from result row to result row.
10+
11+
Also, in `where` or `having` conditions, you have to change the expression (not just parameters) if you want to NULL instead of a value:
12+
13+
```SQL
14+
a = 7
15+
a IS NULL
16+
```
17+
18+
# Obtaining potential NULL values from a select
19+
sqlpp11 can determine whether a result field can be null, based on the columns involved and the structure of your query. If in doubt (for instance due to dynamic parts), it will assume that a field can be NULL.
20+
21+
You can check if a field is NULL by calling the `is_null()` method. That's easy.
22+
23+
When it comes to accessing the value, there are two options, though. These can be controlled by the connection class and the columns of your tables.
24+
25+
## Option 1: No conversion operator
26+
```C++
27+
class connection: public sqlpp::connection
28+
{
29+
public:
30+
using _traits = ::sqlpp::make_traits<::sqlpp::no_value_t,
31+
::sqlpp::tag::enforce_null_result_treatment
32+
>;
33+
```
34+
If this tag is used in the connection's traits and the respective column does not override it, then there is no conversion operator for fields that can be NULL. You have to access the value through the `value()` method.
35+
If the field is NULL, this method will throw an `sqlpp::exception`.
36+
37+
## Option 2: Conversion operator, converting NULL to trivial value
38+
If the `tag::enforce_null_result_treatment` is not used in the connection class or the respective column uses `tag::enforce_null_result_treatment`, then there is a conversion operator. Both the conversion operator and the `value()` method will not throw in case of a NULL value. Instead, the will return the trivial value for the field's type, e.g. 0 for numbers or "" for texts.
39+
40+
## Alternatives:
41+
One often discussed alternative would be boost::optional or (in the future) std::optional. There is one drawbacks (correct me if I am wrong, please):
42+
43+
`optional` cannot be used for binding result values because it is unclear whether there already is a value to bind to.
44+
45+
# Handling NULL in statements
46+
When adding potential NULL values to a statement, you have two options:
47+
48+
## Manually
49+
```C++
50+
auto s = dynamic_select(db, all_of(tab)).from(tab).dynamic_where();
51+
if (i_do_have_a_decent_value_of_alpha)
52+
s.add_where(tab.alpha == alpha);
53+
else
54+
s.add_where(tab.alpha.is_null());
55+
```
56+
57+
## tvin()
58+
`tvin()` is a free function that can be used with `std::string` and build-in types like `int` or `float` or `bool`. `tvin` stands for Trivial Value Is NULL. It is used in combination with `operator==()`, `operator!=()` and `operator=()`. These operators will behave the way they should, e.g.
59+
60+
```C++
61+
select(all_of(tab)).from(tab).where(tab.alpha == sqlpp::tvin(a));
62+
```
63+
64+
This will evaluate to
65+
66+
```SQL
67+
SELECT tab.* FROM tab WHERE alpha = 7;
68+
-- OR
69+
SELECT tab.* FROM tab WHERE alpha IS NULL;
70+
```
71+
72+
Similar with insert:
73+
74+
```C++
75+
insert_into(tab).set(tab.alpha = sqlpp::tvin(a));
76+
```
77+
78+
This will evaluate to
79+
80+
```SQL
81+
INSERT INTO tab (alpha) VALUES(7);
82+
-- OR
83+
INSERT INTO tab (alpha) VALUES(NULL);
84+
```
85+
86+
## Using Column Type
87+
Like to accessing values in select results, setting values can be controlled via the column type:
88+
89+
```C++
90+
struct Alpha
91+
{
92+
struct _name_t;
93+
using _traits = sqlpp::make_traits<sqlpp::bigint,
94+
// ...
95+
sqlpp::tag::trivial_value_is_null>;
96+
};
97+
```
98+
With this tag, you do not need to use `tvin()` for operators `=`, `==`, `!=`. It is used automatically. It translates operator `!` into `IS NULL`.
99+
100+
**Hint**: Please be aware that trivial_value_is_null will not work with parameters in prepared statements.

docs/New-Features.md

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# New Features
2+
3+
There are a bunch of new features, that are not fully documented yet. If you would like to contribute documentation, please let me know.
4+
5+
## Preprocessor generator for columns/tables
6+
You'll need boost 1.50 or greater to use this feature by niXman:
7+
8+
```C++
9+
#include <sqlpp11/ppgen.h>
10+
SQLPP_DECLARE_TABLE(
11+
(tab_person)
12+
,
13+
(id , int , SQLPP_AUTO_INCREMENT)
14+
(name , varchar(255), SQLPP_NOT_NULL )
15+
(feature, int , SQLPP_NOT_NULL )
16+
)
17+
```
18+
19+
See `examples/ppgen.hpp`.
20+
21+
## Union
22+
Unions are now supported. The arguments need to have the same names and types in their columns.
23+
24+
```C++
25+
db(select(t.alpha).from(t).where(true)
26+
.union_distinct(select(f.epsilon.as(t.alpha)).from(f).where(true)));
27+
db(select(t.alpha).from(t).where(true)
28+
.union_all(select(f.epsilon.as(t.alpha)).from(f).where(true)));
29+
```
30+
31+
## With
32+
sqlpp11 supports common table expressions:
33+
34+
```C++
35+
auto x = sqlpp::cte(sqlpp::alias::x).as(select(all_of(t)).from(t));
36+
37+
db(with(x)(select(x.alpha).from(x).where(true)));
38+
```
39+
## Custom Queries
40+
This allows you to combine building blocks of sqlpp11 into custom queries, for instance a SELECT..INTO which is not supported yet.
41+
42+
```C++
43+
// A custom (select ... into) with adjusted return type
44+
// The first argument with a return type is the select,
45+
// but the custom query is really an insert. Thus, we tell it so.
46+
db(custom_query(select(all_of(t)).from(t), into(f))
47+
.with_result_type_of(insert_into(f)));
48+
```
49+
50+
## Schema-qualified tables
51+
sqlpp11 assumes that you're connection addresses one database, normally. But you can tell it about other databases using the `sqlpp::schema_t` and instantiating schema-qualified tables with it:
52+
53+
```C++
54+
auto schema = db.attach("lorem_ipsum");
55+
auto s = schema_qualified_table(schema, TabSample{}).as(sqlpp::alias::x)
56+
// s now represents "lorem_ipsum.tab_sample as x"
57+
// s can be used like any other table in the code
58+
```

0 commit comments

Comments
 (0)