0% found this document useful (0 votes)
36 views145 pages

Persisting C++ Classes in Relational Databases With ODB - Boris Kolpackov - CppCon 2014

Uploaded by

alan88w
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
36 views145 pages

Persisting C++ Classes in Relational Databases With ODB - Boris Kolpackov - CppCon 2014

Uploaded by

alan88w
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 145

Persisting C++ Classes in Relational Databases

with ODB

Boris Kolpackov

Code Synthesis

v1.0, Sep 2014

CODE
SYNTHESIS

-1-
ODB, an ORM for C++

• Part I: Introduction and Basic Operations


• Part II: Advanced Technique and Mechanisms

-2-
Object Relational Mapping

What’s an ORM, anyway?

-3-
Object Relational Mapping

Why ORM?
• Object-oriented vs relational mismatch
• Type and name safety
• Parameter binding and result set extraction
• Database schema evolution

-4-
Manual Schema Evolution

ALTER TABLE person


ADD COLUMN age
INTEGER UNSIGNED NOT NULL DEFAULT 0

-5-
Object Relational Mapping

Why not use an ORM?


• Hides too much
• Shoot yourself in the foot
• Framework
• Fun to roll your own

-6-
sword OCIBindDynamic ( OCIBind *bindp,
OCIError *errhp,
void *ictxp,
OCICallbackInBind (icbfp)(
void *ictxp,
OCIBind *bindp,
ub4 iter,
ub4 index,
void **bufpp,
ub4 *alenp,
ub1 *piecep,
void **indpp ),
void *octxp,
OCICallbackOutBind (ocbfp)(
void *octxp,
OCIBind *bindp,

-7-
Object Relational Mapping

Why Relational?

• Mature and reliable


• Tooling, support, and alternatives
• Flexible

-8-
ODB, and ORM for C++

What’s ODB?

• Three levels
• Not a framework
• No magic
• One-to-one ORM-Database operation mapping

-9-
ODB, and ORM for C++

• Automatic generation of database code from C++ classes


• Target multiple databases
• Database schema evolution

-10-
C++ Standards

C++98 and C++11

• Rvalue references
• Range-based for loop
• std::function and lambdas
• C++11 Standard Library integration
• C++11 in examples

-11-
Databases

Cross-Database

• MySQL
• SQLite
• PostgreSQL
• Oracle
• Microsoft SQL Server

-12-
Platforms and Compilers

Cross-Platform

• Linux, Windows, Mac OS X, Solaris


• GCC, Visual C++, Clang, Sun Studio C++

-13-
-14-
Mobile & Embedded

• ODB + SQLite
• “Hello, World” example is 500Kb
• Cross-compiler friendly
• Android, Raspberry Pi guides

-15-
Performance

High-Performance and Low Overhead

• Prepared statements, including custom queries


• Caching of connections, statements, and buffers
• Low-level native database C APIs
• Zero per-object memory overhead

-16-
Performance

High-Performance and Low Overhead

• Prepared statements, including custom queries


• Caching of connections, statements, and buffers
• Low-level native database C APIs
• Zero per-object memory overhead

Load performance
• SQLite — 60,000 object per second — 17 µs per object
• PostgreSQL — 15,000 objects per second — 65 µs per object

-16-
License

Dual-Licensed

• GPL + commercial license


• Can be used without restrictions within your organization
• License exceptions for open source projects

• ODB License
• www.codesynthesis.com/products/odb/license.xhtml

-17-
C++ Support

ODB is implemented as a GCC plugin

-18-
C++ Support

ODB is implemented as a GCC plugin

• Mature, portable, and readily available


• One of the most complete C++11 implementations

-18-
C++ Support

C++ in, C++ out

-19-
C++ Support

C++ in, C++ out

Use any C++ compiler to build your application

-19-
C++ Support

C++ in, C++ out

Use any C++ compiler to build your application

Yes, even Sun Studio

-19-
C++ Support

Standard C++ In

-20-
C++ Support

Standard C++ In
Standard C++ Out

-20-
Persistent Class

enum class status {open, confirmed, closed};

class bug
{
public:
...

private:
unsigned long long id_;

status status_;
std::string summary_;
std::string description_;
};

-21-
Persistent Class
.
#include <odb/core.hxx>

#pragma db object
class bug
{
...
private:
friend class odb::access;
bug () {}

#pragma db id auto
unsigned long long id_;

status status_;
std::string summary_;
std::string description_;
};

-22-
Persistent Class
.
#include <odb/core.hxx>

#pragma db object
class bug
{
...
private:
friend class odb::access;
bug () {}

#pragma db id auto
unsigned long long id_;

status status_;
std::string summary_;
std::string description_;
};

-22-
Persistent Class
.
#include <odb/core.hxx>

#pragma db object
class bug
{
...
private:
friend class odb::access;
bug () {}

#pragma db id auto
unsigned long long id_;

status status_;
std::string summary_;
std::string description_;
};

-22-
Persistent Class
.
#include <odb/core.hxx>

#pragma db object
class bug
{
...
private:
friend class odb::access;
bug () {}

#pragma db id auto
unsigned long long id_;

status status_;
std::string summary_;
std::string description_;
};

-22-
Persistent Class
.
#include <odb/core.hxx>

#pragma db object
class bug
{
...
private:
friend class odb::access;
bug () {}

#pragma db id auto
unsigned long long id_;

status status_;
std::string summary_;
std::string description_;
};

-22-
Persistent Class
.
#include <odb/core.hxx>

#pragma db object
class bug
{
...
private:
friend class odb::access;
bug () {}

#pragma db id auto
unsigned long long id_;

status status_;
std::string summary_;
std::string description_;
};

-22-
Persistent Class
.
#pragma db object
class bug
{
public:
unsigned long long id () const;
void id (unsigned long long);

status get_status () const;


status& setStatus ();

std::string& summary_please ();

...
private:

#pragma db id auto
unsigned long long id_;
...
-23-
Persistent Class
.
#pragma db object
class bug
{
public:
unsigned long long id () const;
void id (unsigned long long);

status get_status () const;


status& setStatus ();

std::string& summary_please ();

...
private:

#pragma db id auto
unsigned long long id_;
...
-23-
Persistent Class

class bug
{
...
private:
unsigned long long id_;

...
};

#ifdef ODB_COMPILER
# pragma db object(bug)
# pragma db member(bug::id_) id auto
#endif

-24-
Persistent Class

// bug.hxx
class bug
{
...
private:
unsigned long long id_;

...
};

// bug-mapping.hxx
#pragma db object(bug)
#pragma db member(bug::id_) id auto

-25-
Workflow
..
#include
C++ Header

C++ Source

C++ Compiler

-26-
Workflow
..
#include
C++ Header

ODB Compiler C++ Source

C++ Compiler

-26-
Workflow
..
#include
C++ Header

ODB Compiler C++ Source

C++ Source C++ Header

C++ Compiler

-26-
Workflow
..
#include
C++ Header

ODB Compiler C++ Source

Database
C++ Source C++ Header
Schema

C++ Compiler

-26-
Workflow
..
#include
C++ Header

ODB Compiler C++ Source

Database #include
C++ Source C++ Header
Schema

C++ Compiler

-26-
Workflow
..
#include
C++ Header

ODB Compiler C++ Source

Database #include
C++ Source C++ Header
Schema

C++ Compiler

-26-
ODB Compiler

$ odb --database pgsql bug.hxx

-27-
ODB Compiler

$ odb --database pgsql bug.hxx

$ ls
bug.hxx
bug-odb.cxx
bug-odb.hxx
bug-odb.ixx

-27-
ODB Compiler

$ odb -I/opt/boost-latest -DENABLE_LASER_BEAMS ...

-28-
ODB Compiler

$ odb -I/opt/boost-latest -DENABLE_LASER_BEAMS ...

$ odb --std c++11 --default-pointer std::shared_ptr ...

-28-
ODB Compiler
$ odb --generate-schema -d mysql bug.hxx

-29-
ODB Compiler
$ odb --generate-schema -d mysql bug.hxx

$ ls
bug.hxx
bug-odb.cxx
bug-odb.hxx
bug-odb.ixx
bug.sql

-29-
ODB Compiler
$ odb --generate-schema -d mysql bug.hxx

$ ls
bug.hxx
bug-odb.cxx
bug-odb.hxx
bug-odb.ixx
bug.sql

$ cat bug.sql

-29-
ODB Compiler
$ odb --generate-schema -d mysql bug.hxx

$ ls
bug.hxx
bug-odb.cxx
bug-odb.hxx
bug-odb.ixx
bug.sql

$ cat bug.sql

CREATE TABLE bug (


id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
status ENUM(’open’, ’confirmed’, ’closed’) NOT NULL,
summary TEXT NOT NULL,
description TEXT NOT NULL)
-29-
Database

#include <odb/pgsql/database.hxx>

odb::pgsql::database db (”bugger”, // user


”secret”, // password
”bugs”); // database

-30-
Database

#include <odb/pgsql/database.hxx>

odb::pgsql::database db (”bugger”, // user


”secret”, // password
”bugs”); // database

#include <odb/sqlite/database.hxx>

odb::sqlite::database db (”bugs.db”); // database

-30-
Database

#include <odb/pgsql/database.hxx>

odb::pgsql::database db (”bugger”, // user


”secret”, // password
”bugs”); // database

#include <odb/sqlite/database.hxx>

odb::sqlite::database db (”bugs.db”); // database

#include <odb/database.hxx>

void do_it (odb::database& db);

-30-
Database Schema

• Automatically generated
• Map to a custom schema

-31-
Generated Schema

• Standalone SQL file


• Embedded into generated C++

-32-
Generated Schema

• Standalone SQL file


• Embedded into generated C++

#include <odb/schema-catalog.hxx>

transaction t (db.begin ());


schema_catalog::create_schema (db);
t.commit ();

-32-
Custom Schema
• Map classes to tables
• Map data members to columns
• Map C++ types to database types

-33-
Custom Schema
• Map classes to tables
• Map data members to columns
• Map C++ types to database types

#pragma db object table(”bugs”)


class bug
{
#pragma db id auto column(”bug_id”)
unsigned long long id_;

#pragma db column(”bug_status”) type(”SMALLINT”)


status status_;

...
};

-33-
Making Objects Persistent

.
bug b (open,
”Support for DB2”,
”ODB does not yet support IBM DB2.”);

transaction t (db.begin ());

db.persist (b);

t.commit ();

-34-
Making Objects Persistent

.
bug b (open,
”Support for DB2”,
”ODB does not yet support IBM DB2.”);

transaction t (db.begin ());

db.persist (b);

t.commit ();

-34-
Transactions
.
try
{
transaction t (db.begin ());

db.persist (b1);
db.persist (b2);

t.commit ();
}
catch (const odb::connection_lost&)
{
// Try again.
...
}

-35-
Transactions
.
try
{
transaction t (db.begin ());

db.persist (b1);
db.persist (b2);

t.commit ();
}
catch (const odb::connection_lost&)
{
// Try again.
...
}

-35-
Transactions
.
try
{
transaction t (db.begin ());

db.persist (b1);
db.persist (b2);

t.commit ();
}
catch (const odb::connection_lost&)
{
// Try again.
...
}

-35-
Making Objects Persistent
.
bug b (open,
”Support for DB2”,
”ODB does not yet support IBM DB2.”);

transaction t (db.begin ());


t.tracer (odb::stderr_tracer);

unsigned long long id = db.persist (b);

t.commit ();

-36-
Making Objects Persistent
.
bug b (open,
”Support for DB2”,
”ODB does not yet support IBM DB2.”);

transaction t (db.begin ());


t.tracer (odb::stderr_tracer);

unsigned long long id = db.persist (b);

t.commit ();

-36-
Making Objects Persistent
.
bug b (open,
”Support for DB2”,
”ODB does not yet support IBM DB2.”);

transaction t (db.begin ());


t.tracer (odb::stderr_tracer);

unsigned long long id = db.persist (b);

t.commit ();

=> INSERT INTO bug (


id,
status,
summary,
description)
VALUES (DEFAULT, $1, $2, $3)
RETURNING id
-36-
Loading Persistent Objects
.
transaction t (db.begin ());

std::shared_ptr<bug> b (db.load<bug> (id));

bug b;
db.load (id, b);

t.commit ();

-37-
Loading Persistent Objects
.
transaction t (db.begin ());

std::shared_ptr<bug> b (db.load<bug> (id));

bug b;
db.load (id, b);

t.commit ();

-37-
Loading Persistent Objects
.
transaction t (db.begin ());

std::shared_ptr<bug> b (db.load<bug> (id));

bug b;
db.load (id, b);

t.commit ();

=> SELECT
status,
summary,
description
FROM bug WHERE id = $1

-37-
Updating Persistent Objects
.
transaction t (db.begin ());

std::shared_ptr<bug> b (db.load<bug> (id));


b->status (confirmed);
db.update (b);

t.commit ();

-38-
Updating Persistent Objects
.
transaction t (db.begin ());

std::shared_ptr<bug> b (db.load<bug> (id));


b->status (confirmed);
db.update (b);

t.commit ();

=> UPDATE bug SET


status = $1,
summary = $2,
description = $3
WHERE id = $4

-38-
Querying the Database

.
typedef odb::query<bug> query;
typedef odb::result<bug> result;

result r = ...

for (result::iterator i (r.begin()); i != r.end(); ++i)


...

for (bug& b: r)
...

-39-
Querying the Database

.
typedef odb::query<bug> query;
typedef odb::result<bug> result;

result r = ...

for (result::iterator i (r.begin()); i != r.end(); ++i)


...

for (bug& b: r)
...

-39-
Querying the Database

.
typedef odb::query<bug> query;
typedef odb::result<bug> result;

result r = ...

for (result::iterator i (r.begin()); i != r.end(); ++i)


...

for (bug& b: r)
...

-39-
Querying the Database

.
typedef odb::query<bug> query;
typedef odb::result<bug> result;

transaction t (db.begin ());

result r (db.query<bug> (query::status == open));

for (const bug& b: r)


cout << b.id () << ” ” << b.summary () << endl;

t.commit ();

-40-
Querying the Database

.
typedef odb::query<bug> query;
typedef odb::result<bug> result;

transaction t (db.begin ());

result r (db.query<bug> (query::status == open));

for (const bug& b: r)


cout << b.id () << ” ” << b.summary () << endl;

t.commit ();

-40-
Querying the Database
.
typedef odb::query<bug> query;

transaction t (db.begin ());

for (auto& b: db.query<bug> (query::status == open))


...

t.commit ();

-41-
Querying the Database
.
typedef odb::query<bug> query;

transaction t (db.begin ());

for (auto& b: db.query<bug> (query::status == open))


...

t.commit ();

=> SELECT
id
status,
summary,
description
FROM bug WHERE status = $1

-41-
Querying the Database
.
db.query<bug> (query::status == open ||
query::status == confirmed);

status s;
query q (query::status == query::_ref (s));

s = open;
db.query<bug> (q); // status == open

s = closed;
db.query<bug> (q); // status == closed

db.query<bug> (”status = ” + query::_val (open));

db.query<bug> (”stats = ” + query::_val (123));

-42-
Querying the Database
.
db.query<bug> (query::status == open ||
query::status == confirmed);

status s;
query q (query::status == query::_ref (s));

s = open;
db.query<bug> (q); // status == open

s = closed;
db.query<bug> (q); // status == closed

db.query<bug> (”status = ” + query::_val (open));

db.query<bug> (”stats = ” + query::_val (123));

-42-
Querying the Database
.
db.query<bug> (query::status == open ||
query::status == confirmed);

status s;
query q (query::status == query::_ref (s));

s = open;
db.query<bug> (q); // status == open

s = closed;
db.query<bug> (q); // status == closed

db.query<bug> (”status = ” + query::_val (open));

db.query<bug> (”stats = ” + query::_val (123));

-42-
Querying the Database
.
db.query<bug> (query::status == open ||
query::status == confirmed);

status s;
query q (query::status == query::_ref (s));

s = open;
db.query<bug> (q); // status == open

s = closed;
db.query<bug> (q); // status == closed

db.query<bug> (”status = ” + query::_val (open));

db.query<bug> (”stats = ” + query::_val (123));

-42-
Querying the Database
.
db.query<bug> (query::status == open ||
query::status == confirmed);

status s;
query q (query::status == query::_ref (s));

s = open;
db.query<bug> (q); // status == open

s = closed;
db.query<bug> (q); // status == closed

db.query<bug> (”status = ” + query::_val (open));

db.query<bug> (”stats = ” + query::_val (123));

-42-
Deleting Persistent Objects
.
transaction t (db.begin ());

db.erase<bug> (id);

bug b = ...;
db.erase (b);

db.erase_query<bug> (query::status == closed);

t.commit ();

-43-
Deleting Persistent Objects
.
transaction t (db.begin ());

db.erase<bug> (id);

bug b = ...;
db.erase (b);

db.erase_query<bug> (query::status == closed);

t.commit ();

-43-
Deleting Persistent Objects
.
transaction t (db.begin ());

db.erase<bug> (id);

bug b = ...;
db.erase (b);

db.erase_query<bug> (query::status == closed);

t.commit ();

-43-
Deleting Persistent Objects
.
transaction t (db.begin ());

db.erase<bug> (id);

bug b = ...;
db.erase (b);

db.erase_query<bug> (query::status == closed);

t.commit ();

=> DELETE FROM bug WHERE id = $1

-43-
Adding Timestamps
.
#pragma db object
class bug
{
...

#pragma db id auto
unsigned long long id_;

status status_;
std::string summary_;
std::string description_;

boost::posix_time::ptime created_;
boost::posix_time::ptime updated_;
};

-44-
Adding Timestamps
.
#pragma db object
class bug
{
...

#pragma db id auto
unsigned long long id_;

status status_;
std::string summary_;
std::string description_;

boost::posix_time::ptime created_;
boost::posix_time::ptime updated_;
};

-44-
Profiles

• Generic integration mechanism


• Covers smart pointers, containers, and value types
• ODB includes profiles for Boost and Qt
• You can add your own profiles

odb -d pgsql -p boost bug.hxx

odb -d pgsql -p qt bug.hxx

-45-
Boost Profile

• uuid
• date_time
• optional

-46-
NULL Semantics

#pragma db object
class bug
{
...

boost::optional<std::string> description_;
};

CREATE TABLE bug (


...
description TEXT NULL)

-47-
Qt Profile

• Basic types: QString, QUuid, QByteArray


• Date-time types: QDate, QTime, QDateTime

-48-
Adding Creation and Modification Dates (Qt)

#pragma db object
class Bug
{
...

#pragma db id auto
unsigned long long id_;

Status status_;
QString summary_;
QString description_;

QDateTime created_;
QDateTime updated_;
};

-49-
Containers

• Standard: vector, list, set, map, etc


• C++11: array, unordered (hashtable), etc
• Boost: unordered, multi_index
• Qt: QList, QVector, QMap, QSet, QHash, etc
• Easy to support custom containers

-50-
Adding Comments and Tags
.
#pragma db object
class bug
{
...

#pragma db id auto
unsigned long long id_;

status status_;
std::string summary_;
std::string description_;

boost::posix_time::ptime created_;
boost::posix_time::ptime updated_;

std::vector<std::string> comments_;
std::unordered_set<std::string> tags_;
};
-51-
Adding Comments and Tags (Qt)
.
#pragma db object
class Bug
{
...

#pragma db id auto
unsigned long long id_;

Status status_;
QString summary_;
QString description_;

QDateTime created_;
QDateTime updated_;

QList<QString> comments_;
QHash<QString> tags_;
};
-52-
Composite Value Types

• Class or struct type


• Mapped to more than one database column
• Contains composite values, containers, pointers to objects
• Can be used as an object id

-53-
Extending Comments
.
#pragma db value
class comment
{
...

std::string text_;
boost::posix_time::ptime created_;
};

#pragma db object
class bug
{
...

std::vector<comment> comments_;
};

-54-
Relationships

• Relationships are represented as pointers to objects


• Standard: raw, auto_ptr, tr1::shared_ptr
• C++11: std::shared_ptr, std::unique_ptr
• Boost: boost::shared_ptr
• Qt: QSharedPointer
• Easy to support custom smart pointers

-55-
Adding User Object

#pragma db object
class user
{
...

#pragma db id
std::string email_;

std::string first_;
std::string last_;
};

-56-
Adding Bug Reporter

.
#pragma db object
class bug
{
...

std::shared_ptr<user> reporter_;
};

-57-
Adding Bug Reporter

.
#pragma db object
class bug
{
...

std::shared_ptr<user> reporter_;
};

unidirectional to-one relationship

-57-
Adding Bug List
.
#pragma db object
class user
{
...

#pragma db id
std::string email_;

std::string first_name_;
std::string last_name_;

std::vector<std::shared_ptr<bug>> reported_bugs_;
};

-58-
Adding Bug List
.
#pragma db object
class user
{
...

#pragma db id
std::string email_;

std::string first_name_;
std::string last_name_;

std::vector<std::shared_ptr<bug>> reported_bugs_;
};

bidirectional many-to-one relationship

-58-
We Have a Problem

#pragma db object
class user
{
...

std::vector<std::shared_ptr<bug>> reported_bugs_;
};

#pragma db object
class bug
{
...

std::shared_ptr<user> reporter_;
};

-59-
We Have a Problem
.
#pragma db object
class user
{
...

std::vector<std::weak_ptr<bug>> reported_bugs_;
};

#pragma db object
class bug
{
...

std::shared_ptr<user> reporter_;
};

-60-
Another Problem
.
CREATE TABLE bug (
...
reporter TEXT NULL,
CONSTRAINT reporter_fk
FOREIGN KEY (reporter)
REFERENCES user (email));

CREATE TABLE user_reported_bugs (


...
bug_id BIGINT NULL,
CONSTRAINT bug_id_fk
FOREIGN KEY (bug_id)
REFERENCES bug (id));

-61-
Another Problem
.
CREATE TABLE bug (
...
reporter TEXT NULL,
CONSTRAINT reporter_fk
FOREIGN KEY (reporter)
REFERENCES user (email));

CREATE TABLE user_reported_bugs (


...
bug_id BIGINT NULL,
CONSTRAINT bug_id_fk
FOREIGN KEY (bug_id)
REFERENCES bug (id));

-61-
Another Problem
.
#pragma db object
class user
{
...

#pragma db inverse(reporter_)
std::vector<std::weak_ptr<bug>> reported_bugs_;
};

#pragma db object
class bug
{
...

std::shared_ptr<user> reporter_;
};

-62-
Adding Bug Reporter and Bug List (Qt)
#pragma db object
class User
{
...

#pragma db inverse(reporter_)
QList<QWeakPointer<Bug>> reportedBugs_;
};

#pragma db object
class Bug
{
...

QSharedPointer<User> reporter_;
};

-63-
Relationships in Queries

.
typedef odb::query<bug> query;

db.query<bug> (query::reporter->last == ”Doe”);

-64-
Multi-Database Support

• Static
• Dynamic

-65-
Multi-Database Support

• Static
• Dynamic
• Mixed

-65-
Multi-Database Support

$ odb -m static -d common -d sqlite -d pgsql bug.hxx

-66-
Multi-Database Support

$ odb -m static -d common -d sqlite -d pgsql bug.hxx

$ ls
bug.hxx
bug-odb.cxx bug-odb-sqlite.cxx bug-odb-pgsql.cxx
bug-odb.hxx bug-odb-sqlite.hxx bug-odb-pgsql.cxx
bug-odb.ixx bug-odb-sqlite.ixx bug-odb-pgsql.cxx

-66-
Static Multi-Database Support
.
#include ”bug-odb-pgsql.hxx”
#include ”bug-odb-sqlite.hxx”

odb::pgsql::database store (...);


odb::sqlite::database cache (...);

std::shared_ptr<bug> b;
{
odb::transaction t (cache.begin ());
b = cache.find<bug> (id);
t.commit ();
}

if (b == nullptr)
{
odb::transaction t (store.begin ());
b = store.load<bug> (id);
t.commit ();
} -67-
Static Multi-Database Support
.
#include ”bug-odb-pgsql.hxx”
#include ”bug-odb-sqlite.hxx”

odb::pgsql::database store (...);


odb::sqlite::database cache (...);

std::shared_ptr<bug> b;
{
odb::transaction t (cache.begin ());
b = cache.find<bug> (id);
t.commit ();
}

if (b == nullptr)
{
odb::transaction t (store.begin ());
b = store.load<bug> (id);
t.commit ();
} -67-
Static Multi-Database Support
.
#include ”bug-odb-pgsql.hxx”
#include ”bug-odb-sqlite.hxx”

odb::pgsql::database store (...);


odb::sqlite::database cache (...);

std::shared_ptr<bug> b;
{
odb::transaction t (cache.begin ());
b = cache.find<bug> (id);
t.commit ();
}

if (b == nullptr)
{
odb::transaction t (store.begin ());
b = store.load<bug> (id);
t.commit ();
} -67-
Static Multi-Database Support
.
#include ”bug-odb-pgsql.hxx”
#include ”bug-odb-sqlite.hxx”

odb::pgsql::database store (...);


odb::sqlite::database cache (...);

std::shared_ptr<bug> b;
{
odb::transaction t (cache.begin ());
b = cache.find<bug> (id);
t.commit ();
}

if (b == nullptr)
{
odb::transaction t (store.begin ());
b = store.load<bug> (id);
t.commit ();
} -67-
Static Multi-Database Support
.
#include ”bug-odb-pgsql.hxx”
#include ”bug-odb-sqlite.hxx”

odb::pgsql::database store (...);


odb::sqlite::database cache (...);

std::shared_ptr<bug> b;
{
odb::transaction t (cache.begin ());
b = cache.find<bug> (id);
t.commit ();
}

if (b == nullptr)
{
odb::transaction t (store.begin ());
b = store.load<bug> (id);
t.commit ();
} -67-
Dynamic Multi-Database Support
.
#include ”bug-odb.hxx”

std::shared_ptr<bug>
find_bug (odb::database& db, unsigned long long id)
{
odb::transaction t (db.begin ());
std::shared_ptr<bug> r (db.find<bug> (id));
t.commit ();
return r;
}

odb::pgsql::database store (...);


odb::sqlite::database cache (...);

std::shared_ptr<bug> b (find_bug (cache, id));

if (b == nullptr)
b = find_bug (store, id);
-68-
Dynamic Multi-Database Support
.
#include ”bug-odb.hxx”

std::shared_ptr<bug>
find_bug (odb::database& db, unsigned long long id)
{
odb::transaction t (db.begin ());
std::shared_ptr<bug> r (db.find<bug> (id));
t.commit ();
return r;
}

odb::pgsql::database store (...);


odb::sqlite::database cache (...);

std::shared_ptr<bug> b (find_bug (cache, id));

if (b == nullptr)
b = find_bug (store, id);
-68-
Dynamic Multi-Database Support
.
#include ”bug-odb.hxx”

std::shared_ptr<bug>
find_bug (odb::database& db, unsigned long long id)
{
odb::transaction t (db.begin ());
std::shared_ptr<bug> r (db.find<bug> (id));
t.commit ();
return r;
}

odb::pgsql::database store (...);


odb::sqlite::database cache (...);

std::shared_ptr<bug> b (find_bug (cache, id));

if (b == nullptr)
b = find_bug (store, id);
-68-
Dynamic Multi-Database Support
.
#include ”bug-odb.hxx”

std::shared_ptr<bug>
find_bug (odb::database& db, unsigned long long id)
{
odb::transaction t (db.begin ());
std::shared_ptr<bug> r (db.find<bug> (id));
t.commit ();
return r;
}

odb::pgsql::database store (...);


odb::sqlite::database cache (...);

std::shared_ptr<bug> b (find_bug (cache, id));

if (b == nullptr)
b = find_bug (store, id);
-68-
Dynamic Loading
void
load_db (const std::string& db_name)
{
#ifdef _WIN32
string dll (”bug-” + db_name + ”.dll”);
HMODULE h (LoadLibraryA (dll.c_str ()));
#else
string so (”libbug-” + db_name + ”.so”);
void* h (dlopen (so.c_str (), RTLD_NOW));
#endif

if (h == 0)
{
// Handle error.
}
}

-69-
Database Schema Evolution

• No magic
• Simple, easy to understand building blocks
• Schema migration
• Data migration

-70-
Object Model Version
.
#pragma db model version(1, 1)

#pragma db object
class bug
{
...
};

#pragma db model version(1, 2)

#pragma db object
class bug
{
...

std::string platform_;
};
-71-
Object Model Version
.
#pragma db model version(1, 1)

#pragma db object
class bug
{
...
};

#pragma db model version(1, 2)

#pragma db object
class bug
{
...

std::string platform_;
};
-71-
Changelog

• XML file (human reviewable)


• Base model + changeset for each version
• Stored in source code repository

-72-
Changelog

• XML file (human reviewable)


• Base model + changeset for each version
• Stored in source code repository

<changeset version=”2”>
<alter-table name=”bug”>
<add-column name=”platform” type=”TEXT” null=”false”/>
</alter-table>
</changeset>

<model version=”1”>
...
</model>

-72-
Schema Migration

• SQL files or embedded into C++ code


• Pre and Post (bug-002-pre.sql and bug-002-post.sql)
• Pre-migration relaxes the schema
• Post-migration tightens it back

-73-
Schema Migration

• SQL files or embedded into C++ code


• Pre and Post (bug-002-pre.sql and bug-002-post.sql)
• Pre-migration relaxes the schema
• Post-migration tightens it back
• Data migration fits between the two

-73-
Schema Migration

.
/* bug-002-pre.sql */

ALTER TABLE bug


ADD COLUMN platform TEXT NULL;

/* bug-002-post.sql */

ALTER TABLE bug


ALTER COLUMN platform SET NOT NULL;

-74-
Schema Migration

.
/* bug-002-pre.sql */

ALTER TABLE bug


ADD COLUMN platform TEXT NULL;

/* bug-002-post.sql */

ALTER TABLE bug


ALTER COLUMN platform SET NOT NULL;

-74-
Data Migration

.
transaction t (db.begin ());

schema_catalog::migrate_schema_pre (db, 2);

for (bug& b: db.query<bug> ())


{
b.platform (”Unknown”);
db.update (b);
}

schema_catalog::migrate_schema_post (db, 2);

t.commit ();

-75-
Data Migration

.
transaction t (db.begin ());

schema_catalog::migrate_schema_pre (db, 2);

for (bug& b: db.query<bug> ())


{
b.platform (”Unknown”);
db.update (b);
}

schema_catalog::migrate_schema_post (db, 2);

t.commit ();

-75-
Data Migration

.
transaction t (db.begin ());

schema_catalog::migrate_schema_pre (db, 2);

for (bug& b: db.query<bug> ())


{
b.platform (”Unknown”);
db.update (b);
}

schema_catalog::migrate_schema_post (db, 2);

t.commit ();

-75-
Data Migration

schema_catalog::data_migration_function (
2,
[] (database& db)
{
for (bug& b: db.query<bug> ())
{
b.platform (”Unknown”);
db.update (b);
}
});

transaction t (db.begin ());


schema_catalog::migrate (db);
t.commit ();

-76-
Schema Evolution
.
#pragma db model version(1, 2)

#pragma db object
class user
{
std::string first_;
std::string last_;
};

#pragma db model version(1, 3)

#pragma db object
class user
{
std::string name_;
};

-77-
Schema Evolution
.
#pragma db model version(1, 2)

#pragma db object
class user
{
std::string first_;
std::string last_;
};

#pragma db model version(1, 3)

#pragma db object
class user
{
std::string name_;
};

-77-
Changelog Diff

+ <changeset version=”3”>
+ <alter-table name=”user”>
+ <add-column name=”name” type=”TEXT” null=”false”/>
+ <drop-column name=”first”/>
+ <drop-column name=”last”/>
+ </alter-table>
+ </changeset>

-78-
Data Migration

.
schema_catalog::data_migration_function (
3,
[] (database& db)
{
for (bug& b: db.query<bug> ())
{
b.name (b.first () + ” ” + b.last ());
db.update (b);
}
});

-79-
Data Migration

.
schema_catalog::data_migration_function (
3,
[] (database& db)
{
for (bug& b: db.query<bug> ())
{
b.name (b.first () + ” ” + b.last ());
db.update (b);
}
});

-79-
Resources

• ODB Page
• www.codesynthesis.com/products/odb/

• ODB Manual
• www.codesynthesis.com/products/odb/doc/manual.xhtml

• Blog
• www.codesynthesis.com/~boris/blog/

-80-

You might also like