Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/Database.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#Introduction
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

* MySQL: https://github.com/rbock/sqlpp11-connector-mysql
* sqlite3: https://github.com/rbock/sqlpp11-connector-sqlite3
* PostgreSQL: https://github.com/matthijs/sqlpp11-connector-postgresql
* STL Container (highly experimental): https://github.com/rbock/sqlpp11-connector-stl

Or you have to write your own connector. Don't worry, it is not that hard.

The api is documented [here](https://github.com/rbock/sqlpp11/blob/master/connector_api/connection.h).
69 changes: 69 additions & 0 deletions docs/Dynamic-Select.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Introduction
_This page explains dynamic select statements. Before studying this page, you should read about [static select statements](Select.md)._

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.

# A Basic Example
So let us construct select query with a dynamic part
```C++
auto s = dynamic_select(db, all_of(foo)).from(foo).dynamic_where();
if (runtimeQuery.id)
s.where.add(foo.id == runtimeQuery.id);
if (!runtimeQuery.name.empty())
s.where.add(foo.name == runtimeQuery.name);
```
Admittedly, a rather lame example (please suggest nicer ones), but anyway, this is what's happening:
```C++
dynamic_select(db, ...)
```
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.

```C++
.dynamic_where();
...
s.where.add(foo.name == runtimeQuery.name);
s.where.add(foo.id == runtimeQuery.id);
```
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.

# Dynamic Select
## Dynamic Columns
If the (some) selected columns are not known at compile time, you can do something like this:
```C++
auto s = dynamic_select(db).dynamic_columns(foo.id).from(foo).unconditionally();
if (someCondition)
s.selected_columns.add(foo.name);
if (someOtherCondition)
s.selected_columns.add(foo.hasFun);
```
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:
```C++
for (const auto& row : db(s))
{
long id = row.id;
if (someCondition)
std::string name = row.at("name");
if (someOtherCondition)
std::string hasFun = row.at("hasFun");
}
```
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.

## Dynamic From
In a static query the compiler can verify whether the `from()` clause is sufficient to support all other aspects of the query.

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!

```C++
auto s = dynamic_select(db, all_of(foo)).dynamic_from(foo).dynamic_where();
if (someOtherCondition)
s.from.add(dynamic_join(bar).on(foo.barId == bar.id));
```

In this example, the user may want to include `bar` into the query.

## Dynamic Where
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`.

## Dynamic Having, Group By, OrderBy, Limit and Offset
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.
10 changes: 10 additions & 0 deletions docs/Exception-Handling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## When to expect an exception

sqlpp11 connectors throw the majority of `sqlpp::exception`s so check your connector's documentation. Generally, you should expect an exception when:

- Connecting to a database
- Preparing a statement
- Executing a statement
- Retrieving and iterating through result rows

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.
86 changes: 86 additions & 0 deletions docs/Functions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
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).

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.

# Member Functions
## in and not_in
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.

```C++
tab.a.in(); // not valid SQL but certainly useful for generic code, evaluates to a false expression (version 0.35 and later).
tab.a.in(x, ...); // x, ... being one or more value expressions
tab.a.in(sub_select); // sub_select being a select expression with one result column of appropriate type.
tab.a.in(sqlpp::value_list(some_container_of_values)); // evaluates to a false expression in case of an empty container
```

## is_null
## like

# Aggregate Functions
The following aggregate functions are supported
* avg
* count
* max
* min
* sum

For example:
```C++
for (const auto& row : db(select(tab.name, avg(tab.value))
.from(tab)
.where(tab.id > 17)
.group_by(tab.name)))
{
std::cerr << row.name << ": " << row.avg << std::endl;
}
```

# Text Functions
## concat
Just use the + operator :-)

# Sub-Query Functions
## exists
## any
## some

# Misc Functions
sqlpp11 offers a few functions that do not mimic SQL but are there to make your life easier.

## value
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
```C++
tab.foo + 17
```
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:
```C++
for (const auto& row : select(sqlpp::value<sqlpp::bigint>(7).as(sql::alias::a)).from(tab)))
{
int64_t a = row.a;
}
```

##value_list
`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.

## verbatim
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:
```C++
select(all_of(tab)).from(tab).where(tab.foo == 42 and sqlpp::verbatim<sqlpp::boolean>("mighty special feature"));
```
_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.

## flatten
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.
The expression templates can get in your way though, if you want to create parts of your query dynamically:
```C++
auto e = (tab.foo == 17);
if (userWantsBar)
e = (tab.bar == 17); // won't compile
```
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).
```C++
auto e = flatten(tab.foo == 17);
if (userWantsBar)
e = flatten(tab.bar == 17); // will compile
```
24 changes: 24 additions & 0 deletions docs/Home.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Introduction
Lets see:
* You know C++?
* You know some SQL?
* You want to use SQL in your C++ program?
* You think C++ and SQL should play well together?
* You know which tables you want to use in a database?
* You can cope with a few template error messages in case something is wrong?

You have come to the right place!

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++.

The following pages will tell you how to use it:
* [Database](Database.md)
* [Tables](Tables.md)
* [Insert](Insert.md)
* [Select](Select.md) <- You might want to read this first as an appetizer
* [Update](Update.md)
* [Remove](Remove.md)
* [Functions](Functions.md)
* [Prepared Statements](Prepared-Statements.md)
* [NULL](NULL.md)
* [New Features](New-Features.md)
18 changes: 18 additions & 0 deletions docs/Insert.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Haven't found the time to document this in any detail, yet, but this is an example:

```C++
db(insert_into(tab).set(tab.gamma = true));
```

This is how you could insert multiple rows at a time:

```C++
auto multi_insert = insert_into(t).columns(t.gamma, t.beta, t.delta);
multi_insert.values.add(t.gamma = true, t.beta = "cheesecake", t.delta = 1);
multi_insert.values.add(t.gamma = sqlpp::default_value, t.beta = sqlpp::default_value,
t.delta = sqlpp::default_value);
multi_insert.values.add(t.gamma = sqlpp::value_or_null(true),
t.beta = sqlpp::value_or_null("pie"),
t.delta = sqlpp::value_or_null<sqlpp::integer>(sqlpp::null));
db(multi_insert);
```
100 changes: 100 additions & 0 deletions docs/NULL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Introduction
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

```SQL
NULL != NULL -> NULL
NULL = NULL -> NULL
```

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.

Also, in `where` or `having` conditions, you have to change the expression (not just parameters) if you want to NULL instead of a value:

```SQL
a = 7
a IS NULL
```

# Obtaining potential NULL values from a select
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.

You can check if a field is NULL by calling the `is_null()` method. That's easy.

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.

## Option 1: No conversion operator
```C++
class connection: public sqlpp::connection
{
public:
using _traits = ::sqlpp::make_traits<::sqlpp::no_value_t,
::sqlpp::tag::enforce_null_result_treatment
>;
```
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.
If the field is NULL, this method will throw an `sqlpp::exception`.

## Option 2: Conversion operator, converting NULL to trivial value
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.

## Alternatives:
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):

`optional` cannot be used for binding result values because it is unclear whether there already is a value to bind to.

# Handling NULL in statements
When adding potential NULL values to a statement, you have two options:

## Manually
```C++
auto s = dynamic_select(db, all_of(tab)).from(tab).dynamic_where();
if (i_do_have_a_decent_value_of_alpha)
s.add_where(tab.alpha == alpha);
else
s.add_where(tab.alpha.is_null());
```

## tvin()
`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.

```C++
select(all_of(tab)).from(tab).where(tab.alpha == sqlpp::tvin(a));
```

This will evaluate to

```SQL
SELECT tab.* FROM tab WHERE alpha = 7;
-- OR
SELECT tab.* FROM tab WHERE alpha IS NULL;
```

Similar with insert:

```C++
insert_into(tab).set(tab.alpha = sqlpp::tvin(a));
```

This will evaluate to

```SQL
INSERT INTO tab (alpha) VALUES(7);
-- OR
INSERT INTO tab (alpha) VALUES(NULL);
```

## Using Column Type
Like to accessing values in select results, setting values can be controlled via the column type:

```C++
struct Alpha
{
struct _name_t;
using _traits = sqlpp::make_traits<sqlpp::bigint,
// ...
sqlpp::tag::trivial_value_is_null>;
};
```
With this tag, you do not need to use `tvin()` for operators `=`, `==`, `!=`. It is used automatically. It translates operator `!` into `IS NULL`.

**Hint**: Please be aware that trivial_value_is_null will not work with parameters in prepared statements.
58 changes: 58 additions & 0 deletions docs/New-Features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# New Features

There are a bunch of new features, that are not fully documented yet. If you would like to contribute documentation, please let me know.

## Preprocessor generator for columns/tables
You'll need boost 1.50 or greater to use this feature by niXman:

```C++
#include <sqlpp11/ppgen.h>
SQLPP_DECLARE_TABLE(
(tab_person)
,
(id , int , SQLPP_AUTO_INCREMENT)
(name , varchar(255), SQLPP_NOT_NULL )
(feature, int , SQLPP_NOT_NULL )
)
```

See `examples/ppgen.hpp`.

## Union
Unions are now supported. The arguments need to have the same names and types in their columns.

```C++
db(select(t.alpha).from(t).where(true)
.union_distinct(select(f.epsilon.as(t.alpha)).from(f).where(true)));
db(select(t.alpha).from(t).where(true)
.union_all(select(f.epsilon.as(t.alpha)).from(f).where(true)));
```

## With
sqlpp11 supports common table expressions:

```C++
auto x = sqlpp::cte(sqlpp::alias::x).as(select(all_of(t)).from(t));

db(with(x)(select(x.alpha).from(x).where(true)));
```
## Custom Queries
This allows you to combine building blocks of sqlpp11 into custom queries, for instance a SELECT..INTO which is not supported yet.

```C++
// A custom (select ... into) with adjusted return type
// The first argument with a return type is the select,
// but the custom query is really an insert. Thus, we tell it so.
db(custom_query(select(all_of(t)).from(t), into(f))
.with_result_type_of(insert_into(f)));
```

## Schema-qualified tables
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:

```C++
auto schema = db.attach("lorem_ipsum");
auto s = schema_qualified_table(schema, TabSample{}).as(sqlpp::alias::x)
// s now represents "lorem_ipsum.tab_sample as x"
// s can be used like any other table in the code
```
Loading