summaryrefslogtreecommitdiff
path: root/doc/manual.xhtml
diff options
context:
space:
mode:
Diffstat (limited to 'doc/manual.xhtml')
-rw-r--r--doc/manual.xhtml27023
1 files changed, 0 insertions, 27023 deletions
diff --git a/doc/manual.xhtml b/doc/manual.xhtml
deleted file mode 100644
index 0b6dc08..0000000
--- a/doc/manual.xhtml
+++ /dev/null
@@ -1,27023 +0,0 @@
-<?xml version="1.0" encoding="iso-8859-1"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
-
-<head>
- <title>C++ Object Persistence with ODB</title>
-
- <meta name="copyright" content="&#169; 2009-2020 Code Synthesis Tools CC"/>
- <meta name="keywords" content="odb,c++,object,persistence,ORM,relational,database,RDBMS,ODBMS,OODBMS"/>
- <meta name="description" content="C++ Object Persistence with ODB"/>
- <meta name="revision" content="2.4"/>
- <meta name="version" content="2.4.0"/>
-
-<!--
-
-If you make changes to this document, follow these stylistic rules
-for consistency.
-
- - Don't use 'object' for instances of non-persistent classes. Use
- 'instance' instead.
-
- - Each overloaded function should still be referred to as function.
- When saying that a function is overloaded, use term 'version',
- for example The persist() function has two overloaded versions.
- Don't use version to refer to individual functions, use function
- instead. The same holds for constructors.
-
- - Use 'object id' and 'object's identifier'. But not other combinations
- of the two.
-
-@@ Check that parts TOCs are up to date.
-
--->
-
- <link rel="stylesheet" type="text/css" href="default.css" />
-
-<style type="text/css">
- pre {
- padding : 0 0 0 0em;
- margin : 0em 0em 0em 0;
-
- font-size : 102%
- }
-
- body {
- min-width: 48em;
- }
-
- h1 {
- font-weight: bold;
- font-size: 200%;
- line-height: 1.2em;
- }
-
- h2 {
- font-weight : bold;
- font-size : 150%;
-
- padding-top : 0.8em;
- }
-
- h3 {
- font-size : 140%;
- padding-top : 0.8em;
- }
-
- /* Force page break for both PDF and HTML (when printing). */
- hr.page-break {
- height: 0;
- width: 0;
- border: 0;
- visibility: hidden;
-
- page-break-after: always;
- }
-
- /* Adjust indentation for three levels. */
- #container {
- max-width: 48em;
- }
-
- #content {
- padding: 0 0.1em 0 4em;
- /*background-color: red;*/
- }
-
- #content h1 {
- margin-left: -2.06em;
- }
-
- #content h2 {
- margin-left: -1.33em;
- }
-
- /* Title page */
-
- #titlepage {
- padding: 2em 0 1em 0;
- border-bottom: 1px solid black;
- }
-
- #titlepage .title {
- font-weight: bold;
- font-size: 200%;
- text-align: center;
- padding: 1em 0 2em 0;
- }
-
- #titlepage p {
- padding-bottom: 1em;
- }
-
- #titlepage #revision {
- padding-bottom: 0em;
- }
-
- /* Lists */
- ul.list li, ol.list li {
- padding-top : 0.3em;
- padding-bottom : 0.3em;
- }
-
- div.img {
- text-align: center;
- padding: 2em 0 2em 0;
- }
-
- /* */
- dl dt {
- padding : 0.8em 0 0 0;
- }
-
- /* TOC */
- table.toc {
- border-style : none;
- border-collapse : separate;
- border-spacing : 0;
-
- margin : 0.2em 0 0.2em 0;
- padding : 0 0 0 0;
- }
-
- table.toc tr {
- padding : 0 0 0 0;
- margin : 0 0 0 0;
- }
-
- table.toc * td, table.toc * th {
- border-style : none;
- margin : 0 0 0 0;
- vertical-align : top;
- }
-
- table.toc * th {
- font-weight : normal;
- padding : 0em 0.1em 0em 0;
- text-align : left;
- white-space : nowrap;
- }
-
- table.toc * table.toc th {
- padding-left : 1em;
- }
-
- table.toc * td {
- padding : 0em 0 0em 0.7em;
- text-align : left;
- }
-
- /* operators table */
- #operators {
- margin: 2em 0 2em 0;
-
- border-collapse : collapse;
- border : 1px solid;
- border-color : #000000;
-
- font-size : 11px;
- line-height : 14px;
- }
-
- #operators th, #operators td {
- border: 1px solid;
- padding : 0.9em 0.9em 0.7em 0.9em;
- }
-
- #operators th {
- background : #cde8f6;
- }
-
- #operators td {
- text-align: left;
- }
-
- /* scenarios table */
- .scenarios {
- margin: 2em 0 2em 0;
-
- border-collapse : collapse;
- border : 1px solid;
- border-color : #000000;
-
- font-size : 11px;
- line-height : 14px;
- }
-
- .scenarios th, .scenarios td {
- border: 1px solid;
- padding : 0.9em 0.9em 0.7em 0.9em;
- }
-
- .scenarios th {
- background : #cde8f6;
- }
-
- .scenarios td {
- text-align: left;
- }
-
- /* specifiers table */
- .specifiers {
- margin: 2em 0 2em 0;
-
- border-collapse : collapse;
- border : 1px solid;
- border-color : #000000;
-
- font-size : 11px;
- line-height : 14px;
- }
-
- .specifiers th, .specifiers td {
- border: 1px solid;
- padding : 0.9em 0.9em 0.7em 0.9em;
- }
-
- .specifiers th {
- background : #cde8f6;
- }
-
- .specifiers td {
- text-align: left;
- }
-
- /* mapping table */
- #mapping {
- margin: 2em 0 2em 0;
-
- border-collapse : collapse;
- border : 1px solid;
- border-color : #000000;
-
- font-size : 11px;
- line-height : 14px;
- }
-
- #mapping th, #mapping td {
- border: 1px solid;
- padding : 0.9em 0.9em 0.7em 0.9em;
- }
-
- #mapping th {
- background : #cde8f6;
- }
-
- #mapping td {
- text-align: left;
- }
-
-</style>
-
-
-</head>
-
-<body>
-<div id="container">
- <div id="content">
-
- <div class="noprint">
-
- <div id="titlepage">
- <div class="title">C++ Object Persistence with ODB</div>
-
- <p>Copyright &#169; 2009-2020 Code Synthesis Tools CC.</p>
-
- <p>Permission is granted to copy, distribute and/or modify this
- document under the terms of the
- <a href="http://www.codesynthesis.com/licenses/fdl-1.3.txt">GNU Free
- Documentation License, version 1.3</a>; with no Invariant Sections,
- no Front-Cover Texts and no Back-Cover Texts.</p>
-
- <!-- REMEMBER TO CHANGE VERSIONS IN THE META TAGS ABOVE! -->
- <p id="revision">Revision 2.4, February 2015</p>
- <p>This revision of the manual describes ODB 2.4.0 and is available
- in the following formats:
- <a href="http://www.codesynthesis.com/products/odb/doc/manual.xhtml">XHTML</a>,
- <a href="http://www.codesynthesis.com/products/odb/doc/odb-manual.pdf">PDF</a>, and
- <a href="http://www.codesynthesis.com/products/odb/doc/odb-manual.ps">PostScript</a>.</p>
- </div>
-
- <hr class="page-break"/>
- <h1>Table of Contents</h1>
-
- <table class="toc">
- <tr>
- <th></th><td><a href="#0">Preface</a>
- <table class="toc">
- <tr><th></th><td><a href="#0.1">About This Document</a></td></tr>
- <tr><th></th><td><a href="#0.2">More Information</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th colspan="2"><a href="#I">PART I OBJECT-RELATIONAL MAPPING</a></th>
- </tr>
-
- <tr>
- <th>1</th><td><a href="#1">Introduction</a>
- <table class="toc">
- <tr><th>1.1</th><td><a href="#1.1">Architecture and Workflow</a></td></tr>
- <tr><th>1.2</th><td><a href="#1.2">Benefits</a></td></tr>
- <tr><th>1.3</th><td><a href="#1.3">Supported C++ Standards</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>2</th><td><a href="#2">Hello World Example</a>
- <table class="toc">
- <tr><th>2.1</th><td><a href="#2.1">Declaring Persistent Classes</a></td></tr>
- <tr><th>2.2</th><td><a href="#2.2">Generating Database Support Code</a></td></tr>
- <tr><th>2.3</th><td><a href="#2.3">Compiling and Running</a></td></tr>
- <tr><th>2.4</th><td><a href="#2.4">Making Objects Persistent</a></td></tr>
- <tr><th>2.5</th><td><a href="#2.5">Querying the Database for Objects</a></td></tr>
- <tr><th>2.6</th><td><a href="#2.6">Updating Persistent Objects</a></td></tr>
- <tr><th>2.7</th><td><a href="#2.7">Defining and Using Views</a></td></tr>
- <tr><th>2.8</th><td><a href="#2.8">Deleting Persistent Objects</a></td></tr>
- <tr><th>2.9</th><td><a href="#2.9">Changing Persistent Classes</a></td></tr>
- <tr><th>2.10</th><td><a href="#2.10">Accessing Multiple Databases</a></td></tr>
- <tr><th>2.11</th><td><a href="#2.11">Summary</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>3</th><td><a href="#3">Working with Persistent Objects</a>
- <table class="toc">
- <tr><th>3.1</th><td><a href="#3.1">Concepts and Terminology</a></td></tr>
- <tr><th>3.2</th><td><a href="#3.2">Declaring Persistent Objects and Values</a></td></tr>
- <tr><th>3.3</th><td><a href="#3.3">Object and View Pointers</a></td></tr>
- <tr><th>3.4</th><td><a href="#3.4">Database</a></td></tr>
- <tr><th>3.5</th><td><a href="#3.5">Transactions</a></td></tr>
- <tr><th>3.6</th><td><a href="#3.6">Connections</a></td></tr>
- <tr><th>3.7</th><td><a href="#3.7">Error Handling and Recovery</a></td></tr>
- <tr><th>3.8</th><td><a href="#3.8">Making Objects Persistent</a></td></tr>
- <tr><th>3.9</th><td><a href="#3.9">Loading Persistent Objects</a></td></tr>
- <tr><th>3.10</th><td><a href="#3.10">Updating Persistent Objects</a></td></tr>
- <tr><th>3.11</th><td><a href="#3.11">Deleting Persistent Objects</a></td></tr>
- <tr><th>3.12</th><td><a href="#3.12">Executing Native SQL Statements</a></td></tr>
- <tr><th>3.13</th><td><a href="#3.13">Tracing SQL Statement Execution</a></td></tr>
- <tr><th>3.14</th><td><a href="#3.14">ODB Exceptions</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>4</th><td><a href="#4">Querying the Database</a>
- <table class="toc">
- <tr><th>4.1</th><td><a href="#4.1">ODB Query Language</a></td></tr>
- <tr><th>4.2</th><td><a href="#4.2">Parameter Binding</a></td></tr>
- <tr><th>4.3</th><td><a href="#4.3">Executing a Query</a></td></tr>
- <tr><th>4.4</th><td><a href="#4.4">Query Result</a></td></tr>
- <tr><th>4.5</th><td><a href="#4.5">Prepared Queries</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>5</th><td><a href="#5">Containers</a>
- <table class="toc">
- <tr><th>5.1</th><td><a href="#5.1">Ordered Containers</a></td></tr>
- <tr><th>5.2</th><td><a href="#5.2">Set and Multiset Containers</a></td></tr>
- <tr><th>5.3</th><td><a href="#5.3">Map and Multimap Containers</a></td></tr>
- <tr>
- <th>5.4</th><td><a href="#5.4">Change-Tracking Containers</a>
- <table class="toc">
- <tr><th>5.4.1</th><td><a href="#5.4.1">Change-Tracking <code>vector</code></a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>5.5</th><td><a href="#5.5">Using Custom Containers</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>6</th><td><a href="#6">Relationships</a>
- <table class="toc">
- <tr>
- <th>6.1</th><td><a href="#6.1">Unidirectional Relationships</a>
- <table class="toc">
- <tr><th>6.1.1</th><td><a href="#6.1.1">To-One Relationships</a></td></tr>
- <tr><th>6.1.2</th><td><a href="#6.1.2">To-Many Relationships</a></td></tr>
- </table>
- </td>
- </tr>
- <tr>
- <th>6.2</th><td><a href="#6.2">Bidirectional Relationships</a>
- <table class="toc">
- <tr><th>6.2.1</th><td><a href="#6.2.1">One-to-One Relationships</a></td></tr>
- <tr><th>6.2.2</th><td><a href="#6.2.2">One-to-Many Relationships</a></td></tr>
- <tr><th>6.2.3</th><td><a href="#6.2.3">Many-to-Many Relationships</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>6.3</th><td><a href="#6.3">Circular Relationships</a></td></tr>
- <tr><th>6.4</th><td><a href="#6.4">Lazy Pointers</a></td></tr>
- <tr><th>6.5</th><td><a href="#6.5">Using Custom Smart Pointers</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>7</th><td><a href="#7">Value Types</a>
- <table class="toc">
- <tr><th>7.1</th><td><a href="#7.1">Simple Value Types</a></td></tr>
- <tr>
- <th>7.2</th><td><a href="#7.2">Composite Value Types</a>
- <table class="toc">
- <tr><th>7.2.1</th><td><a href="#7.2.1">Composite Object Ids</a></td></tr>
- <tr><th>7.2.2</th><td><a href="#7.2.2">Composite Value Column and Table Names</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>7.3</th><td><a href="#7.3">Pointers and <code>NULL</code> Value Semantics</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>8</th><td><a href="#8">Inheritance</a>
- <table class="toc">
- <tr><th>8.1</th><td><a href="#8.1">Reuse Inheritance</a></td></tr>
- <tr>
- <th>8.2</th><td><a href="#8.2">Polymorphism Inheritance</a>
- <table class="toc">
- <tr><th>8.2.1</th><td><a href="#8.2.1">Performance and Limitations</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>8.3</th><td><a href="#8.3">Mixed Inheritance</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>9</th><td><a href="#9">Sections</a>
- <table class="toc">
- <tr><th>9.1</th><td><a href="#9.1">Sections and Inheritance</a></td></tr>
- <tr><th>9.2</th><td><a href="#9.2">Sections and Optimistic Concurrency</a></td></tr>
- <tr><th>9.3</th><td><a href="#9.3">Sections and Lazy Pointers</a></td></tr>
- <tr><th>9.4</th><td><a href="#9.4">Sections and Change-Tracking Containers</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>10</th><td><a href="#10">Views</a>
- <table class="toc">
- <tr><th>10.1</th><td><a href="#10.1">Object Views</a></td></tr>
- <tr><th>10.2</th><td><a href="#10.2">Object Loading Views</a></td></tr>
- <tr><th>10.3</th><td><a href="#10.3">Table Views</a></td></tr>
- <tr><th>10.4</th><td><a href="#10.4">Mixed Views</a></td></tr>
- <tr><th>10.5</th><td><a href="#10.5">View Query Conditions</a></td></tr>
- <tr><th>10.6</th><td><a href="#10.6">Native Views</a></td></tr>
- <tr><th>10.7</th><td><a href="#10.7">Other View Features and Limitations</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>11</th><td><a href="#11">Session</a>
- <table class="toc">
- <tr><th>11.1</th><td><a href="#11.1">Object Cache</a></td></tr>
- <tr><th>11.2</th><td><a href="#11.2">Custom Sessions</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>12</th><td><a href="#12">Optimistic Concurrency</a></td>
- </tr>
-
- <tr>
- <th>13</th><td><a href="#13">Database Schema Evolution</a>
- <table class="toc">
- <tr><th>13.1</th><td><a href="#13.1">Object Model Version and Changelog</a></td></tr>
- <tr><th>13.2</th><td><a href="#13.2">Schema Migration</a></td></tr>
- <tr>
- <th>13.3</th><td><a href="#13.3">Data Migration</a>
- <table class="toc">
- <tr><th>13.3.1</th><td><a href="#13.3.1">Immediate Data Migration</a></td></tr>
- <tr><th>13.3.2</th><td><a href="#13.3.2">Gradual Data Migration</a></td></tr>
- </table>
- </td>
- </tr>
- <tr>
- <th>13.4</th><td><a href="#13.4">Soft Object Model Changes</a>
- <table class="toc">
- <tr><th>13.4.1</th><td><a href="#13.4.1">Reuse Inheritance Changes</a></td></tr>
- <tr><th>13.4.2</th><td><a href="#13.4.2">Polymorphism Inheritance Changes</a></td></tr>
- </table>
- </td>
- </tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>14</th><td><a href="#14">ODB Pragma Language</a>
- <table class="toc">
- <tr>
- <th>14.1</th><td><a href="#14.1">Object Type Pragmas</a>
- <table class="toc">
- <tr><th>14.1.1</th><td><a href="#14.1.1"><code>table</code></a></td></tr>
- <tr><th>14.1.2</th><td><a href="#14.1.2"><code>pointer</code></a></td></tr>
- <tr><th>14.1.3</th><td><a href="#14.1.3"><code>abstract</code></a></td></tr>
- <tr><th>14.1.4</th><td><a href="#14.1.4"><code>readonly</code></a></td></tr>
- <tr><th>14.1.5</th><td><a href="#14.1.5"><code>optimistic</code></a></td></tr>
- <tr><th>14.1.6</th><td><a href="#14.1.6"><code>no_id</code></a></td></tr>
- <tr><th>14.1.7</th><td><a href="#14.1.7"><code>callback</code></a></td></tr>
- <tr><th>14.1.8</th><td><a href="#14.1.8"><code>schema</code></a></td></tr>
- <tr><th>14.1.9</th><td><a href="#14.1.9"><code>polymorphic</code></a></td></tr>
- <tr><th>14.1.10</th><td><a href="#14.1.10"><code>session</code></a></td></tr>
- <tr><th>14.1.11</th><td><a href="#14.1.11"><code>definition</code></a></td></tr>
- <tr><th>14.1.12</th><td><a href="#14.1.12"><code>transient</code></a></td></tr>
- <tr><th>14.1.13</th><td><a href="#14.1.13"><code>sectionable</code></a></td></tr>
- <tr><th>14.1.14</th><td><a href="#14.1.14"><code>deleted</code></a></td></tr>
- <tr><th>14.1.15</th><td><a href="#14.1.15"><code>bulk</code></a></td></tr>
- </table>
- </td>
- </tr>
- <tr>
- <th>14.2</th><td><a href="#14.2">View Type Pragmas</a>
- <table class="toc">
- <tr><th>14.2.1</th><td><a href="#14.2.1"><code>object</code></a></td></tr>
- <tr><th>14.2.2</th><td><a href="#14.2.2"><code>table</code></a></td></tr>
- <tr><th>14.2.3</th><td><a href="#14.2.3"><code>query</code></a></td></tr>
- <tr><th>14.2.4</th><td><a href="#14.2.4"><code>pointer</code></a></td></tr>
- <tr><th>14.2.5</th><td><a href="#14.2.5"><code>callback</code></a></td></tr>
- <tr><th>14.2.6</th><td><a href="#14.2.6"><code>definition</code></a></td></tr>
- <tr><th>14.2.7</th><td><a href="#14.2.7"><code>transient</code></a></td></tr>
- </table>
- </td>
- </tr>
- <tr>
- <th>14.3</th><td><a href="#14.3">Value Type Pragmas</a>
- <table class="toc">
- <tr><th>14.3.1</th><td><a href="#14.3.1"><code>type</code></a></td></tr>
- <tr><th>14.3.2</th><td><a href="#14.3.2"><code>id_type</code></a></td></tr>
- <tr><th>14.3.3</th><td><a href="#14.3.3"><code>null</code>/<code>not_null</code></a></td></tr>
- <tr><th>14.3.4</th><td><a href="#14.3.4"><code>default</code></a></td></tr>
- <tr><th>14.3.5</th><td><a href="#14.3.5"><code>options</code></a></td></tr>
- <tr><th>14.3.6</th><td><a href="#14.3.6"><code>readonly</code></a></td></tr>
- <tr><th>14.3.7</th><td><a href="#14.3.7"><code>definition</code></a></td></tr>
- <tr><th>14.3.8</th><td><a href="#14.3.8"><code>transient</code></a></td></tr>
- <tr><th>14.3.9</th><td><a href="#14.3.9"><code>unordered</code></a></td></tr>
- <tr><th>14.3.10</th><td><a href="#14.3.10"><code>index_type</code></a></td></tr>
- <tr><th>14.3.11</th><td><a href="#14.3.11"><code>key_type</code></a></td></tr>
- <tr><th>14.3.12</th><td><a href="#14.3.12"><code>value_type</code></a></td></tr>
- <tr><th>14.3.13</th><td><a href="#14.3.13"><code>value_null</code>/<code>value_not_null</code></a></td></tr>
- <tr><th>14.3.14</th><td><a href="#14.3.14"><code>id_options</code></a></td></tr>
- <tr><th>14.3.15</th><td><a href="#14.3.15"><code>index_options</code></a></td></tr>
- <tr><th>14.3.16</th><td><a href="#14.3.16"><code>key_options</code></a></td></tr>
- <tr><th>14.3.17</th><td><a href="#14.3.17"><code>value_options</code></a></td></tr>
- <tr><th>14.3.18</th><td><a href="#14.3.18"><code>id_column</code></a></td></tr>
- <tr><th>14.3.19</th><td><a href="#14.3.19"><code>index_column</code></a></td></tr>
- <tr><th>14.3.20</th><td><a href="#14.3.20"><code>key_column</code></a></td></tr>
- <tr><th>14.3.21</th><td><a href="#14.3.21"><code>value_column</code></a></td></tr>
- </table>
- </td>
- </tr>
- <tr>
- <th>14.4</th><td><a href="#14.4">Data Member Pragmas</a>
- <table class="toc">
- <tr><th>14.4.1</th><td><a href="#14.4.1"><code>id</code></a></td></tr>
- <tr><th>14.4.2</th><td><a href="#14.4.2"><code>auto</code></a></td></tr>
- <tr><th>14.4.3</th><td><a href="#14.4.3"><code>type</code></a></td></tr>
- <tr><th>14.4.4</th><td><a href="#14.4.4"><code>id_type</code></a></td></tr>
- <tr><th>14.4.5</th><td><a href="#14.4.5"><code>get</code>/<code>set</code>/<code>access</code></a></td></tr>
- <tr><th>14.4.6</th><td><a href="#14.4.6"><code>null</code>/<code>not_null</code></a></td></tr>
- <tr><th>14.4.7</th><td><a href="#14.4.7"><code>default</code></a></td></tr>
- <tr><th>14.4.8</th><td><a href="#14.4.8"><code>options</code></a></td></tr>
- <tr><th>14.4.9</th><td><a href="#14.4.9"><code>column</code> (object, composite value)</a></td></tr>
- <tr><th>14.4.10</th><td><a href="#14.4.10"><code>column</code> (view)</a></td></tr>
- <tr><th>14.4.11</th><td><a href="#14.4.11"><code>transient</code></a></td></tr>
- <tr><th>14.4.12</th><td><a href="#14.4.12"><code>readonly</code></a></td></tr>
- <tr><th>14.4.13</th><td><a href="#14.4.13"><code>virtual</code></a></td></tr>
- <tr><th>14.4.14</th><td><a href="#14.4.14"><code>inverse</code></a></td></tr>
- <tr><th>14.4.15</th><td><a href="#14.4.15"><code>on_delete</code></a></td></tr>
- <tr><th>14.4.16</th><td><a href="#14.4.16"><code>version</code></a></td></tr>
- <tr><th>14.4.17</th><td><a href="#14.4.17"><code>index</code></a></td></tr>
- <tr><th>14.4.18</th><td><a href="#14.4.18"><code>unique</code></a></td></tr>
- <tr><th>14.4.19</th><td><a href="#14.4.19"><code>unordered</code></a></td></tr>
- <tr><th>14.4.20</th><td><a href="#14.4.20"><code>table</code></a></td></tr>
- <tr><th>14.4.21</th><td><a href="#14.4.21"><code>load</code>/<code>update</code></a></td></tr>
- <tr><th>14.4.22</th><td><a href="#14.4.22"><code>section</code></a></td></tr>
- <tr><th>14.4.23</th><td><a href="#14.4.23"><code>added</code></a></td></tr>
- <tr><th>14.4.24</th><td><a href="#14.4.24"><code>deleted</code></a></td></tr>
- <tr><th>14.4.25</th><td><a href="#14.4.25"><code>index_type</code></a></td></tr>
- <tr><th>14.4.26</th><td><a href="#14.4.26"><code>key_type</code></a></td></tr>
- <tr><th>14.4.27</th><td><a href="#14.4.27"><code>value_type</code></a></td></tr>
- <tr><th>14.4.28</th><td><a href="#14.4.28"><code>value_null</code>/<code>value_not_null</code></a></td></tr>
- <tr><th>14.4.29</th><td><a href="#14.4.29"><code>id_options</code></a></td></tr>
- <tr><th>14.4.30</th><td><a href="#14.4.30"><code>index_options</code></a></td></tr>
- <tr><th>14.4.31</th><td><a href="#14.4.31"><code>key_options</code></a></td></tr>
- <tr><th>14.4.32</th><td><a href="#14.4.32"><code>value_options</code></a></td></tr>
- <tr><th>14.4.33</th><td><a href="#14.4.33"><code>id_column</code></a></td></tr>
- <tr><th>14.4.34</th><td><a href="#14.4.34"><code>index_column</code></a></td></tr>
- <tr><th>14.4.35</th><td><a href="#14.4.35"><code>key_column</code></a></td></tr>
- <tr><th>14.4.36</th><td><a href="#14.4.36"><code>value_column</code></a></td></tr>
- </table>
- </td>
- </tr>
- <tr>
- <th>14.5</th><td><a href="#14.5">Namespace Pragmas</a>
- <table class="toc">
- <tr><th>14.5.1</th><td><a href="#14.5.1"><code>pointer</code></a></td></tr>
- <tr><th>14.5.2</th><td><a href="#14.5.2"><code>table</code></a></td></tr>
- <tr><th>14.5.3</th><td><a href="#14.5.3"><code>schema</code></a></td></tr>
- <tr><th>14.5.4</th><td><a href="#14.5.4"><code>session</code></a></td></tr>
- </table>
- </td>
- </tr>
- <tr>
- <th>14.6</th><td><a href="#14.6">Object Model Pragmas</a>
- <table class="toc">
- <tr><th>14.6.1</th><td><a href="#14.6.1"><code>version</code></a></td></tr>
- </table>
- </td>
- </tr>
- <tr>
- <th>14.7</th><td><a href="#14.7">Index Definition Pragmas</a></td>
- </tr>
- <tr>
- <th>14.8</th><td><a href="#14.8">Database Type Mapping Pragmas</a></td>
- </tr>
- <tr>
- <th>14.9</th><td><a href="#14.9">C++ Compiler Warnings</a>
- <table class="toc">
- <tr><th>14.9.1</th><td><a href="#14.9.1">GNU C++</a></td></tr>
- <tr><th>14.9.2</th><td><a href="#14.9.2">Visual C++</a></td></tr>
- <tr><th>14.9.3</th><td><a href="#14.9.3">Sun C++</a></td></tr>
- <tr><th>14.9.4</th><td><a href="#14.9.4">IBM XL C++</a></td></tr>
- <tr><th>14.9.5</th><td><a href="#14.9.5">HP aC++</a></td></tr>
- <tr><th>14.9.6</th><td><a href="#14.9.6">Clang</a></td></tr>
- </table>
- </td>
- </tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>15</th><td><a href="#15">Advanced Techniques and Mechanisms</a>
- <table class="toc">
- <tr><th>15.1</th><td><a href="#15.1">Transaction Callbacks</a></td></tr>
- <tr><th>15.2</th><td><a href="#15.2">Persistent Class Template Instantiations</a></td></tr>
- <tr><th>15.3</th><td><a href="#15.3">Bulk Database Operations</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th colspan="2"><a href="#II">PART II DATABASE SYSTEMS</a></th>
- </tr>
-
- <tr>
- <th>16</th><td><a href="#16">Multi-Database Support</a>
- <table class="toc">
- <tr><th>16.1</th><td><a href="#16.1">Static Multi-Database Support</a></td></tr>
- <tr>
- <th>16.2</th><td><a href="#16.2">Dynamic Multi-Database Support</a>
- <table class="toc">
- <tr><th>16.2.2</th><td><a href="#16.2.2">16.2.2 Dynamic Loading of Database Support Code</a></td></tr>
- </table>
- </td>
- </tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>17</th><td><a href="#17">MySQL Database</a>
- <table class="toc">
- <tr>
- <th>17.1</th><td><a href="#17.1">MySQL Type Mapping</a>
- <table class="toc">
- <tr><th>17.1.1</th><td><a href="#17.1.1">String Type Mapping</a></td></tr>
- <tr><th>17.1.2</th><td><a href="#17.1.2">Binary Type Mapping</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>17.2</th><td><a href="#17.2">MySQL Database Class</a></td></tr>
- <tr><th>17.3</th><td><a href="#17.3">MySQL Connection and Connection Factory</a></td></tr>
- <tr><th>17.4</th><td><a href="#17.4">MySQL Exceptions</a></td></tr>
- <tr>
- <th>17.5</th><td><a href="#17.5">MySQL Limitations</a>
- <table class="toc">
- <tr><th>17.5.1</th><td><a href="#17.5.1">Foreign Key Constraints</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>17.6</th><td><a href="#17.6">MySQL Index Definition</a></td></tr>
- <tr><th>17.7</th><td><a href="#17.7">MySQL Stored Procedures</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>18</th><td><a href="#18">SQLite Database</a>
- <table class="toc">
- <tr>
- <th>18.1</th><td><a href="#18.1">SQLite Type Mapping</a>
- <table class="toc">
- <tr><th>18.1.1</th><td><a href="#18.1.1">String Type Mapping</a></td></tr>
- <tr><th>18.1.2</th><td><a href="#18.1.2">Binary Type Mapping</a></td></tr>
- <tr><th>18.1.3</th><td><a href="#18.1.3">Incremental <code>BLOB</code>/<code>TEXT</code> I/O</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>18.2</th><td><a href="#18.2">SQLite Database Class</a></td></tr>
- <tr><th>18.3</th><td><a href="#18.3">SQLite Connection and Connection Factory</a></td></tr>
- <tr><th>18.4</th><td><a href="#18.4">SQLite Exceptions</a></td></tr>
- <tr>
- <th>18.5</th><td><a href="#18.5">SQLite Limitations</a>
- <table class="toc">
- <tr><th>18.5.1</th><td><a href="#18.5.1">Query Result Caching</a></td></tr>
- <tr><th>18.5.2</th><td><a href="#18.5.2">Automatic Assignment of Object Ids</a></td></tr>
- <tr><th>18.5.3</th><td><a href="#18.5.3">Foreign Key Constraints</a></td></tr>
- <tr><th>18.5.4</th><td><a href="#18.5.4">Constraint Violations</a></td></tr>
- <tr><th>18.5.5</th><td><a href="#18.5.5">Sharing of Queries</a></td></tr>
- <tr><th>18.5.6</th><td><a href="#18.5.6">Forced Rollback</a></td></tr>
- <tr><th>18.5.7</th><td><a href="#18.5.7">Database Schema Evolution</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>18.6</th><td><a href="#18.6">SQLite Index Definition</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>19</th><td><a href="#19">PostgreSQL Database</a>
- <table class="toc">
- <tr>
- <th>19.1</th><td><a href="#19.1">PostgreSQL Type Mapping</a>
- <table class="toc">
- <tr><th>19.1.1</th><td><a href="#19.1.1">String Type Mapping</a></td></tr>
- <tr><th>19.1.2</th><td><a href="#19.1.2">Binary Type and <code>UUID</code> Mapping</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>19.2</th><td><a href="#19.2">PostgreSQL Database Class</a></td></tr>
- <tr><th>19.3</th><td><a href="#19.3">PostgreSQL Connection and Connection Factory</a></td></tr>
- <tr><th>19.4</th><td><a href="#19.4">PostgreSQL Exceptions</a></td></tr>
- <tr>
- <th>19.5</th><td><a href="#19.5">PostgreSQL Limitations</a>
- <table class="toc">
- <tr><th>19.5.1</th><td><a href="#19.5.1">Query Result Caching</a></td></tr>
- <tr><th>19.5.2</th><td><a href="#19.5.2">Foreign Key Constraints</a></td></tr>
- <tr><th>19.5.3</th><td><a href="#19.5.3">Unique Constraint Violations</a></td></tr>
- <tr><th>19.5.4</th><td><a href="#19.5.4">Date-Time Format</a></td></tr>
- <tr><th>19.5.5</th><td><a href="#19.5.5">Timezones</a></td></tr>
- <tr><th>19.5.6</th><td><a href="#19.5.6"><code>NUMERIC</code> Type Support</a></td></tr>
- <tr><th>19.5.7</th><td><a href="#19.5.7">Bulk Operations Support</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>19.6</th><td><a href="#19.6">PostgreSQL Index Definition</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>20</th><td><a href="#20">Oracle Database</a>
- <table class="toc">
- <tr>
- <th>20.1</th><td><a href="#20.1">Oracle Type Mapping</a>
- <table class="toc">
- <tr><th>20.1.1</th><td><a href="#20.1.1">String Type Mapping</a></td></tr>
- <tr><th>20.1.2</th><td><a href="#20.1.2">Binary Type Mapping</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>20.2</th><td><a href="#20.2">Oracle Database Class</a></td></tr>
- <tr><th>20.3</th><td><a href="#20.3">Oracle Connection and Connection Factory</a></td></tr>
- <tr><th>20.4</th><td><a href="#20.4">Oracle Exceptions</a></td></tr>
- <tr>
- <th>20.5</th><td><a href="#20.5">Oracle Limitations</a>
- <table class="toc">
- <tr><th>20.5.1</th><td><a href="#20.5.1">Identifier Truncation</a></td></tr>
- <tr><th>20.5.2</th><td><a href="#20.5.2">Query Result Caching</a></td></tr>
- <tr><th>20.5.3</th><td><a href="#20.5.3">Foreign Key Constraints</a></td></tr>
- <tr><th>20.5.4</th><td><a href="#20.5.4">Unique Constraint Violations</a></td></tr>
- <tr><th>20.5.5</th><td><a href="#20.5.5">Large <code>FLOAT</code> and <code>NUMBER</code> Types</a></td></tr>
- <tr><th>20.5.6</th><td><a href="#20.5.6">Timezones</a></td></tr>
- <tr><th>20.5.7</th><td><a href="#20.5.7"><code>LONG</code> Types</a></td></tr>
- <tr><th>20.5.8</th><td><a href="#20.5.8">LOB Types and By-Value Accessors/Modifiers</a></td></tr>
- <tr><th>20.5.9</th><td><a href="#20.5.9">Database Schema Evolution</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>20.6</th><td><a href="#20.6">Oracle Index Definition</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>21</th><td><a href="#21">Microsoft SQL Server Database</a>
- <table class="toc">
- <tr>
- <th>21.1</th><td><a href="#21.1">SQL Server Type Mapping</a>
- <table class="toc">
- <tr><th>21.1.1</th><td><a href="#21.1.1">String Type Mapping</a></td></tr>
- <tr><th>21.1.2</th><td><a href="#21.1.2">Binary Type and <code>UNIQUEIDENTIFIER</code> Mapping</a></td></tr>
- <tr><th>21.1.3</th><td><a href="#21.1.3"><code>ROWVERSION</code> Mapping</a></td></tr>
- <tr><th>21.1.4</th><td><a href="#21.1.4">Long String and Binary Types</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>21.2</th><td><a href="#21.2">SQL Server Database Class</a></td></tr>
- <tr><th>21.3</th><td><a href="#21.3">SQL Server Connection and Connection Factory</a></td></tr>
- <tr><th>21.4</th><td><a href="#21.4">SQL Server Exceptions</a></td></tr>
- <tr>
- <th>21.5</th><td><a href="#21.5">SQL Server Limitations</a>
- <table class="toc">
- <tr><th>21.5.1</th><td><a href="#21.5.1">Query Result Caching</a></td></tr>
- <tr><th>21.5.2</th><td><a href="#21.5.2">Foreign Key Constraints</a></td></tr>
- <tr><th>21.5.3</th><td><a href="#21.5.3">Unique Constraint Violations</a></td></tr>
- <tr><th>21.5.4</th><td><a href="#21.5.4">Multi-threaded Windows Applications</a></td></tr>
- <tr><th>21.5.5</th><td><a href="#21.5.5">Affected Row Count and DDL Statements</a></td></tr>
- <tr><th>21.5.6</th><td><a href="#21.5.6">Long Data and Auto Object Ids, <code>ROWVERSION</code></a></td></tr>
- <tr><th>21.5.7</th><td><a href="#21.5.7">Long Data and By-Value Accessors/Modifiers</a></td></tr>
- <tr><th>21.5.8</th><td><a href="#21.5.8">Bulk Update and <code>ROWVERSION</code></a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>21.6</th><td><a href="#21.6">SQL Server Index Definition</a></td></tr>
- <tr><th>21.7</th><td><a href="#21.7">SQL Server Stored Procedures</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th colspan="2"><a href="#III">PART III PROFILES</a></th>
- </tr>
-
- <tr>
- <th>22</th><td><a href="#22">Profiles Introduction</a></td>
- </tr>
-
- <tr>
- <th>23</th><td><a href="#23">Boost Profile</a>
- <table class="toc">
- <tr><th>23.1</th><td><a href="#23.1">Smart Pointers Library</a></td></tr>
- <tr><th>23.2</th><td><a href="#23.2">Unordered Containers Library</a></td></tr>
- <tr><th>23.3</th><td><a href="#23.3">Multi-Index Container Library</a></td></tr>
- <tr><th>23.4</th><td><a href="#23.4">Optional Library</a></td></tr>
- <tr>
- <th>23.5</th><td><a href="#23.5">Date Time Library</a>
- <table class="toc">
- <tr><th>23.5.1</th><td><a href="#23.5.1">MySQL Database Type Mapping</a></td></tr>
- <tr><th>23.5.2</th><td><a href="#23.5.2">SQLite Database Type Mapping</a></td></tr>
- <tr><th>23.5.3</th><td><a href="#23.5.3">PostgreSQL Database Type Mapping</a></td></tr>
- <tr><th>23.5.4</th><td><a href="#23.5.4">Oracle Database Type Mapping</a></td></tr>
- <tr><th>23.5.5</th><td><a href="#23.5.5">SQL Server Database Type Mapping</a></td></tr>
- </table>
- </td>
- </tr>
- <tr>
- <th>23.6</th><td><a href="#23.6">Uuid Library</a>
- <table class="toc">
- <tr><th>23.6.1</th><td><a href="#23.6.1">MySQL Database Type Mapping</a></td></tr>
- <tr><th>23.6.2</th><td><a href="#23.6.2">SQLite Database Type Mapping</a></td></tr>
- <tr><th>23.6.3</th><td><a href="#23.6.3">PostgreSQL Database Type Mapping</a></td></tr>
- <tr><th>23.6.4</th><td><a href="#23.6.4">Oracle Database Type Mapping</a></td></tr>
- <tr><th>23.6.5</th><td><a href="#23.6.5">SQL Server Database Type Mapping</a></td></tr>
- </table>
- </td>
- </tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>24</th><td><a href="#24">Qt Profile</a>
- <table class="toc">
- <tr>
- <th>24.1</th><td><a href="#24.1">Basic Types Library</a>
- <table class="toc">
- <tr><th>24.1.1</th><td><a href="#24.1.1">MySQL Database Type Mapping</a></td></tr>
- <tr><th>24.1.2</th><td><a href="#24.1.2">SQLite Database Type Mapping</a></td></tr>
- <tr><th>24.1.3</th><td><a href="#24.1.3">PostgreSQL Database Type Mapping</a></td></tr>
- <tr><th>24.1.4</th><td><a href="#24.1.4">Oracle Database Type Mapping</a></td></tr>
- <tr><th>24.1.5</th><td><a href="#24.1.5">SQL Server Database Type Mapping</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>24.2</th><td><a href="#24.2">Smart Pointers Library</a></td></tr>
- <tr>
- <th>24.3</th><td><a href="#24.3">Containers Library</a>
- <table class="toc">
- <tr><th>24.3.1</th><td><a href="#24.3.1">Change-Tracking <code>QList</code></a></td></tr>
- </table>
- </td>
- </tr>
- <tr>
- <th>24.4</th><td><a href="#24.4">Date Time Library</a>
- <table class="toc">
- <tr><th>24.4.1</th><td><a href="#24.4.1">MySQL Database Type Mapping</a></td></tr>
- <tr><th>24.4.2</th><td><a href="#24.4.2">SQLite Database Type Mapping</a></td></tr>
- <tr><th>24.4.3</th><td><a href="#24.4.3">PostgreSQL Database Type Mapping</a></td></tr>
- <tr><th>24.4.4</th><td><a href="#24.4.4">Oracle Database Type Mapping</a></td></tr>
- <tr><th>24.4.5</th><td><a href="#24.4.5">SQL Server Database Type Mapping</a></td></tr>
- </table>
- </td>
- </tr>
- </table>
- </td>
- </tr>
-
- </table>
- </div>
-
- <hr class="page-break"/>
- <h1><a name="0">Preface</a></h1>
-
- <p>As more critical aspects of our lives become dependant on software
- systems, more and more applications are required to save the data
- they work on in persistent and reliable storage. Database management
- systems and, in particular, relational database management systems
- (RDBMS) are commonly used for such storage. However, while the
- application development techniques and programming languages have
- evolved significantly over the past decades, the relational database
- technology in this area stayed relatively unchanged. In particular,
- this led to the now infamous mismatch between the object-oriented
- model used by many modern applications and the relational model still
- used by RDBMS.</p>
-
- <p>While relational databases may be inconvenient to use from modern
- programming languages, they are still the main choice for many
- applications due to their maturity, reliability, as well as the
- availability of tools and alternative implementations.</p>
-
- <p>To allow application developers to utilize relational databases
- from their object-oriented applications, a technique called
- object-relational mapping (ORM) is often used. It involves a
- conversion layer that maps between objects in the application's
- memory and their relational representation in the database. While
- the object-relational mapping code can be written manually,
- automated ORM systems are available for most object-oriented
- programming languages in use today.</p>
-
- <p>ODB is an ORM system for the C++ programming language. It was
- designed and implemented with the following main goals:</p>
-
- <ul class="list">
- <li>Provide a fully-automatic ORM system. In particular, the
- application developer should not have to manually write any
- mapping code, neither for persistent classes nor for their
- data member. </li>
-
- <li>Provide clean and easy to use object-oriented persistence
- model and database APIs that support the development of realistic
- applications for a wide variety of domains.</li>
-
- <li>Provide a portable and thread-safe implementation. ODB should be
- written in standard C++ and capable of persisting any standard
- C++ classes.</li>
-
- <li>Provide profiles that integrate ODB with type systems of
- widely-used frameworks and libraries such as Qt and Boost.</li>
-
- <li>Provide a high-performance and low overhead implementation. ODB
- should make efficient use of database and application resources.</li>
-
- </ul>
-
-
- <h2><a name="0.1">About This Document</a></h2>
-
- <p>The goal of this manual is to provide you with an understanding
- of the object persistence model and APIs which are implemented by ODB.
- As such, this document is intended for C++ application developers and
- software architects who are looking for a C++ object persistence
- solution. Prior experience with C++ is required to understand
- this document. A basic understanding of relational database systems
- is advantageous but not expected or required.</p>
-
-
- <h2><a name="0.2">More Information</a></h2>
-
- <p>Beyond this manual, you may also find the following sources of
- information useful:</p>
-
- <ul class="list">
- <li><a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
- Compiler Command Line Manual.</a></li>
-
- <li>The <code>INSTALL</code> files in the ODB source packages provide
- build instructions for various platforms.</li>
-
- <li>The <code>odb-examples</code> package contains a collection of
- examples and a README file with an overview of each example.</li>
-
- <li>The <a href="http://www.codesynthesis.com/mailman/listinfo/odb-users">odb-users</a>
- mailing list is the place to ask technical questions about ODB.
- Furthermore, the searchable
- <a href="http://www.codesynthesis.com/pipermail/odb-users/">archives</a>
- may already have answers to some of your questions.</li>
-
- </ul>
-
-
- <!-- PART -->
-
-
- <hr class="page-break"/>
- <h1><a name="I">PART I&nbsp;&nbsp;
- <span style="font-weight: normal;">OBJECT-RELATIONAL MAPPING</span></a></h1>
-
- <p>Part I describes the essential database concepts, APIs, and tools that
- together comprise the object-relational mapping for C++ as implemented
- by ODB. It consists of the following chapters.</p>
-
- <table class="toc">
- <tr><th>1</th><td><a href="#1">Introduction</a></td></tr>
- <tr><th>2</th><td><a href="#2">Hello World Example</a></td></tr>
- <tr><th>3</th><td><a href="#3">Working with Persistent Objects</a></td></tr>
- <tr><th>4</th><td><a href="#4">Querying the Database</a></td></tr>
- <tr><th>5</th><td><a href="#5">Containers</a></td></tr>
- <tr><th>6</th><td><a href="#6">Relationships</a></td></tr>
- <tr><th>7</th><td><a href="#7">Value Types</a></td></tr>
- <tr><th>8</th><td><a href="#8">Inheritance</a></td></tr>
- <tr><th>10</th><td><a href="#10">Views</a></td></tr>
- <tr><th>11</th><td><a href="#11">Session</a></td></tr>
- <tr><th>12</th><td><a href="#12">Optimistic Concurrency</a></td></tr>
- <tr><th>13</th><td><a href="#13">Database Schema Evolution</a></td></tr>
- <tr><th>14</th><td><a href="#14">ODB Pragma Language</a></td></tr>
- </table>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="1">1 Introduction</a></h1>
-
- <p>ODB is an object-relational mapping (ORM) system for C++. It provides
- tools, APIs, and library support that allow you to persist C++ objects
- to a relational database (RDBMS) without having to deal with tables,
- columns, or SQL and without manually writing any of the mapping code.</p>
-
- <p>ODB is highly flexible and customizable. It can either completely
- hide the relational nature of the underlying database or expose
- some of the details as required. For example, you can automatically
- map basic C++ types to suitable SQL types, generate the relational
- database schema for your persistent classes, and use simple, safe,
- and yet powerful object query language instead of SQL. Or you can
- assign SQL types to individual data members, use the existing
- database schema, run native SQL <code>SELECT</code> queries, and
- call stored procedures. In fact, at an extreme, ODB can be used
- as <em>just</em> a convenient way to handle results of native SQL
- queries.</p>
-
- <p>ODB is not a framework. It does not dictate how you should write
- your application. Rather, it is designed to fit into your
- style and architecture by only handling object persistence
- and not interfering with any other functionality. There is
- no common base type that all persistent classes should derive
- from nor are there any restrictions on the data member types
- in persistent classes. Existing classes can be made persistent
- with a few or no modifications.</p>
-
- <p>ODB has been designed for high performance and low memory
- overhead. Prepared statements are used to send and receive
- object state in binary format instead of text which reduces
- the load on the application and the database server. Extensive
- caching of connections, prepared statements, and buffers saves
- time and resources on connection establishment, statement parsing,
- and memory allocations. For each supported database system the
- native C API is used instead of ODBC or higher-level wrapper
- APIs to reduce overhead and provide the most efficient implementation
- for each database operation. Finally, persistent classes have
- zero memory overhead. There are no hidden "database" members
- that each class must have nor are there per-object data structures
- allocated by ODB.</p>
-
- <p>In this chapter we present a high-level overview of ODB.
- We will start with the ODB architecture and then outline the
- workflow of building an application that uses ODB. We will
- then continue by contrasting the drawbacks of the traditional
- way of saving C++ objects to relational databases with the
- benefits of using ODB for object persistence. We conclude the
- chapter by discussing the C++ standards supported by ODB. The
- next chapter takes a more hands-on approach and shows the
- concrete steps necessary to implement object persistence in
- a simple "Hello World" application.</p>
-
- <h2><a name="1.1">1.1 Architecture and Workflow</a></h2>
-
- <p>From the application developer's perspective, ODB
- consists of three main components: the ODB compiler, the common
- runtime library, called <code>libodb</code>, and the
- database-specific runtime libraries, called
- <code>libodb-&lt;database></code>, where &lt;database> is
- the name of the database system this runtime
- is for, for example, <code>libodb-mysql</code>. For instance,
- if the application is going to use the MySQL database for
- object persistence, then the three ODB components that this
- application will use are the ODB compiler, <code>libodb</code>
- and <code>libodb-mysql</code>.</p>
-
- <p>The ODB compiler generates the database support code for
- persistent classes in your application. The input to the ODB
- compiler is one or more C++ header files defining C++ classes
- that you want to make persistent. For each input header file
- the ODB compiler generates a set of C++ source files implementing
- conversion between persistent C++ classes defined in this
- header and their database representation. The ODB compiler
- can also generate a database schema file that creates tables
- necessary to store the persistent classes.</p>
-
- <p>The ODB compiler is a real C++ compiler except that it produces
- C++ instead of assembly or machine code. In particular, it is not
- an ad-hoc header pre-processor that is only capable of recognizing
- a subset of C++. ODB is capable of parsing any standard C++ code.</p>
-
- <p>The common runtime library defines database system-independent
- interfaces that your application can use to manipulate persistent
- objects. The database-specific runtime library provides implementations
- of these interfaces for a concrete database as well as other
- database-specific utilities that are used by the generated code.
- Normally, the application does not use the database-specific
- runtime library directly but rather works with it via the common
- interfaces from <code>libodb</code>. The following diagram shows
- the object persistence architecture of an application that uses
- MySQL as the underlying database system:</p>
-
- <!-- align=center is needed for html2ps -->
- <div class="img" align="center"><img src="odb-arch.png"/></div>
-
- <p>The ODB system also defines two special-purpose languages:
- the ODB Pragma Language and ODB Query Language. The ODB Pragma
- Language is used to communicate various properties of persistent
- classes to the ODB compiler by means of special <code>#pragma</code>
- directives embedded in the C++ header files. It controls aspects
- of the object-relational mapping such as names of tables and columns
- that are used for persistent classes and their members or mapping between
- C++ types and database types.</p>
-
- <p>The ODB Query Language is an object-oriented database query
- language that can be used to search for objects matching
- certain criteria. It is modeled after and is integrated into
- C++ allowing you to write expressive and safe queries that look
- and feel like ordinary C++.</p>
-
- <p>The use of the ODB compiler to generate database support code
- adds an additional step to your application build sequence. The
- following diagram outlines the typical build workflow of an
- application that uses ODB:</p>
-
- <!-- align=center is needed for html2ps -->
- <div class="img" align="center"><img src="odb-flow.png"/></div>
-
- <h2><a name="1.2">1.2 Benefits</a></h2>
-
- <p>The traditional way of saving C++ objects to relational databases
- requires that you manually write code which converts between the database
- and C++ representations of each persistent class. The actions that
- such code usually performs include conversion between C++ values and
- strings or database types, preparation and execution of SQL queries,
- as well as handling the result sets. Writing this code manually has
- the following drawbacks:</p>
-
- <ul class="list">
- <li><b>Difficult and time consuming.</b> Writing database conversion
- code for any non-trivial application requires extensive
- knowledge of the specific database system and its APIs.
- It can also take a considerable amount of time to write
- and maintain. Supporting multi-threaded applications can
- complicate this task even further.</li>
-
- <li><b>Suboptimal performance.</b> Optimal conversion often
- requires writing large amounts of extra code, such as
- parameter binding for prepared statements and caching
- of connections, statements, and buffers. Writing code
- like this in an ad-hoc manner is often too difficult
- and time consuming.</li>
-
- <li><b>Database vendor lock-in.</b> The conversion code is written for
- a specific database which makes it hard to switch to another
- database vendor.</li>
-
- <li><b>Lack of type safety.</b> It is easy to misspell column names or
- pass incompatible values in SQL queries. Such errors will
- only be detected at runtime.</li>
-
- <li><b>Complicates the application.</b> The database conversion code
- often ends up interspersed throughout the application making it
- hard to debug, change, and maintain.</li>
- </ul>
-
- <p>In contrast, using ODB for C++ object persistence has the
- following benefits:</p>
-
- <ul class="list">
- <li><b>Ease of use.</b> ODB automatically generates database conversion
- code from your C++ class declarations and allows you to manipulate
- persistent objects using simple and thread-safe object-oriented
- database APIs.</li>
-
- <li><b>Concise code.</b> With ODB hiding the details of the underlying
- database, the application logic is written using the natural object
- vocabulary instead of tables, columns and SQL. The resulting code
- is simpler and thus easier to read and understand.</li>
-
- <li><b>Optimal performance.</b> ODB has been designed for high performance
- and low memory overhead. All the available optimization techniques,
- such as prepared statements and extensive connection, statement,
- and buffer caching, are used to provide the most efficient
- implementation for each database operation.</li>
-
- <li><b>Database portability.</b> Because the database conversion code
- is automatically generated, it is easy to switch from one database
- vendor to another. In fact, it is possible to test your application
- on several database systems before making a choice.</li>
-
- <li><b>Safety.</b> The ODB object persistence and query APIs are
- statically typed. You use C++ identifiers instead of strings
- to refer to object members and the generated code makes sure
- database and C++ types are compatible. All this helps catch
- programming errors at compile-time rather than at runtime.</li>
-
- <li><b>Maintainability.</b> Automatic code generation minimizes the
- effort needed to adapt the application to changes in persistent
- classes. The database support code is kept separately from the
- class declarations and application logic. This makes the
- application easier to debug and maintain.</li>
- </ul>
-
- <p>Overall, ODB provides an easy to use yet flexible and powerful
- object-relational mapping (ORM) system for C++. Unlike other
- ORM implementations for C++ that still require you to write
- database conversion or member registration code for each
- persistent class, ODB keeps persistent classes purely
- declarative. The functional part, the database conversion
- code, is automatically generated by the ODB compiler from
- these declarations.</p>
-
- <h2><a name="1.3">1.3 Supported C++ Standards</a></h2>
-
- <p>ODB provides support for ISO/IEC C++ 1998/2003 (C++98/03),
- ISO/IEC TR 19768 C++ Library Extensions (C++ TR1), and
- ISO/IEC C++ 2011 (C++11). While the majority of the examples in
- this manual use C++98/03, support for the new functionality and
- library components introduced in TR1 and C++11 are discussed
- throughout the document. The <code>c++11</code> example in the
- <code>odb-examples</code> package also shows ODB support for
- various C++11 features.</p>
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="2">2 Hello World Example</a></h1>
-
- <p>In this chapter we will show how to create a simple C++
- application that relies on ODB for object persistence using
- the traditional "Hello World" example. In particular, we will
- discuss how to declare persistent classes, generate database
- support code, as well as compile and run our application. We
- will also learn how to make objects persistent, load, update
- and delete persistent objects, as well as query the database
- for persistent objects that match certain criteria. The example
- also shows how to define and use views, a mechanism that allows
- us to create projections of persistent objects, database tables,
- or to handle results of native SQL queries or stored procedure
- calls.</p>
-
- <p>The code presented in this chapter is based on the
- <code>hello</code> example which can be found in the
- <code>odb-examples</code> package of the ODB distribution.</p>
-
- <h2><a name="2.1">2.1 Declaring Persistent Classes</a></h2>
-
- <p>In our "Hello World" example we will depart slightly from
- the norm and say hello to people instead of the world. People
- in our application will be represented as objects of C++ class
- <code>person</code> which is saved in <code>person.hxx</code>:</p>
-
- <pre class="cxx">
-// person.hxx
-//
-
-#include &lt;string>
-
-class person
-{
-public:
- person (const std::string&amp; first,
- const std::string&amp; last,
- unsigned short age);
-
- const std::string&amp; first () const;
- const std::string&amp; last () const;
-
- unsigned short age () const;
- void age (unsigned short);
-
-private:
- std::string first_;
- std::string last_;
- unsigned short age_;
-};
- </pre>
-
- <p>In order not to miss anyone whom we need to greet, we would like
- to save the <code>person</code> objects in a database. To achieve this
- we declare the <code>person</code> class as persistent:</p>
-
- <pre class="cxx">
-// person.hxx
-//
-
-#include &lt;string>
-
-#include &lt;odb/core.hxx> // (1)
-
-#pragma db object // (2)
-class person
-{
- ...
-
-private:
- person () {} // (3)
-
- friend class odb::access; // (4)
-
- #pragma db id auto // (5)
- unsigned long id_; // (5)
-
- std::string first_;
- std::string last_;
- unsigned short age_;
-};
- </pre>
-
- <p>To be able to save the <code>person</code> objects in the database
- we had to make five changes, marked with (1) to (5), to the original
- class definition. The first change is the inclusion of the ODB
- header <code>&lt;odb/core.hxx></code>. This header provides a number
- of core ODB declarations, such as <code>odb::access</code>, that
- are used to define persistent classes.</p>
-
- <p>The second change is the addition of <code>db&nbsp;object</code>
- pragma just before the class definition. This pragma tells the
- ODB compiler that the class that follows is persistent. Note
- that making a class persistent does not mean that all objects
- of this class will automatically be stored in the database.
- You would still create ordinary or <em>transient</em> instances
- of this class just as you would before. The difference is that
- now you can make such transient instances persistent, as we will
- see shortly.</p>
-
- <p>The third change is the addition of the default constructor.
- The ODB-generated database support code will use this constructor
- when instantiating an object from the persistent state. Just as we have
- done for the <code>person</code> class, you can make the default
- constructor private or protected if you don't want to make it
- available to the users of your class. Note also that with some
- limitations it is possible to have a persistent class without
- the default constructor.</p>
-
- <p>With the fourth change we make the <code>odb::access</code> class a
- friend of our <code>person</code> class. This is necessary to make
- the default constructor and the data members accessible to the
- database support code. If your class has a public default constructor and
- either public data members or public accessors and modifiers for the
- data members, then the <code>friend</code> declaration is unnecessary.</p>
-
- <p>The final change adds a data member called <code>id_</code> which
- is preceded by another pragma. In ODB every persistent object normally
- has a unique, within its class, identifier. Or, in other words, no two
- persistent instances of the same type have equal identifiers. While it
- is possible to define a persistent class without an object id, the number
- of database operations that can be performed on such a class is limited.
- For our class we use an integer id. The <code>db&nbsp;id auto</code>
- pragma that precedes the <code>id_</code> member tells the ODB compiler
- that the following member is the object's identifier. The
- <code>auto</code> specifier indicates that it is a database-assigned
- id. A unique id will be automatically generated by the database and
- assigned to the object when it is made persistent.</p>
-
- <p>In this example we chose to add an identifier because none of
- the existing members could serve the same purpose. However, if
- a class already has a member with suitable properties, then it
- is natural to use that member as an identifier. For example,
- if our <code>person</code> class contained some form of personal
- identification (SSN in the United States or ID/passport number
- in other countries), then we could use that as an id. Or, if
- we stored an email associated with each person, then we could
- have used that if each person is presumed to have a unique
- email address.</p>
-
- <p>As another example, consider the following alternative version
- of the <code>person</code> class. Here we use one of
- the existing data members as id. Also the data members are kept
- private and are instead accessed via public accessor and modifier
- functions. Finally, the ODB pragmas are grouped together and are
- placed after the class definition. They could have also been moved
- into a separate header leaving the original class completely
- unchanged (for more information on such a non-intrusive conversion
- refer to <a href="#14">Chapter 14, "ODB Pragma Language"</a>).</p>
-
- <pre class="cxx">
-class person
-{
-public:
- person ();
-
- const std::string&amp; email () const;
- void email (const std::string&amp;);
-
- const std::string&amp; get_name () const;
- std::string&amp; set_name ();
-
- unsigned short getAge () const;
- void setAge (unsigned short);
-
-private:
- std::string email_;
- std::string name_;
- unsigned short age_;
-};
-
-#pragma db object(person)
-#pragma db member(person::email_) id
- </pre>
-
- <p>Now that we have the header file with the persistent class, let's
- see how we can generate that database support code.</p>
-
- <h2><a name="2.2">2.2 Generating Database Support Code</a></h2>
-
- <p>The persistent class definition that we created in the previous
- section was particularly light on any code that could actually
- do the job and store the person's data to a database. There
- was no serialization or deserialization code, not even data member
- registration, that you would normally have to write by hand in
- other ORM libraries for C++. This is because in ODB code
- that translates between the database and C++ representations
- of an object is automatically generated by the ODB compiler.</p>
-
- <p>To compile the <code>person.hxx</code> header we created in the
- previous section and generate the support code for the MySQL
- database, we invoke the ODB compiler from a terminal (UNIX) or
- a command prompt (Windows):</p>
-
- <pre class="terminal">
-odb -d mysql --generate-query person.hxx
- </pre>
-
- <p>We will use MySQL as the database of choice in the remainder of
- this chapter, though other supported database systems can be used
- instead.</p>
-
- <p>If you haven't installed the common ODB runtime library
- (<code>libodb</code>) or installed it into a directory where
- C++ compilers don't search for headers by default,
- then you may get the following error:</p>
-
- <pre class="terminal">
-person.hxx:10:24: fatal error: odb/core.hxx: No such file or directory
- </pre>
-
- <p>To resolve this you will need to specify the <code>libodb</code> headers
- location with the <code>-I</code> preprocessor option, for example:</p>
-
- <pre class="terminal">
-odb -I.../libodb -d mysql --generate-query person.hxx
- </pre>
-
- <p>Here <code>.../libodb</code> represents the path to the
- <code>libodb</code> directory.</p>
-
- <p>The above invocation of the ODB compiler produces three C++ files:
- <code>person-odb.hxx</code>, <code>person-odb.ixx</code>,
- <code>person-odb.cxx</code>. You normally don't use types
- or functions contained in these files directly. Rather, all
- you have to do is include <code>person-odb.hxx</code> in
- C++ files where you are performing database operations
- with classes from <code>person.hxx</code> as well as compile
- <code>person-odb.cxx</code> and link the resulting object
- file to your application.</p>
-
- <p>You may be wondering what the <code>--generate-query</code>
- option is for. It instructs the ODB compiler to generate
- optional query support code that we will use later in our
- "Hello World" example. Another option that we will find
- useful is <code>--generate-schema</code>. This option
- makes the ODB compiler generate a fourth file,
- <code>person.sql</code>, which is the database schema
- for the persistent classes defined in <code>person.hxx</code>:</p>
-
- <pre class="terminal">
-odb -d mysql --generate-query --generate-schema person.hxx
- </pre>
-
- <p>The database schema file contains SQL statements that creates
- tables necessary to store the persistent classes. We will learn
- how to use it in the next section.</p>
-
- <p>If you would like to see a list of all the available ODB compiler
- options, refer to the
- <a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
- Compiler Command Line Manual</a>.</p>
-
- <p>Now that we have the persistent class and the database support
- code, the only part that is left is the application code that
- does something useful with all of this. But before we move on to
- the fun part, let's first learn how to build and run an application
- that uses ODB. This way when we have some application code
- to try, there are no more delays before we can run it.</p>
-
- <h2><a name="2.3">2.3 Compiling and Running</a></h2>
-
- <p>Assuming that the <code>main()</code> function with the application
- code is saved in <code>driver.cxx</code> and the database support
- code and schema are generated as described in the previous section,
- to build our application we will first need to compile all the C++
- source files and then link them with two ODB runtime libraries.</p>
-
- <p>On UNIX, the compilation part can be done with the following commands
- (substitute <code>c++</code> with your C++ compiler name; for Microsoft
- Visual Studio setup, see the <code>odb-examples</code> package):</p>
-
- <pre class="terminal">
-c++ -c driver.cxx
-c++ -c person-odb.cxx
- </pre>
-
- <p>Similar to the ODB compilation, if you get an error stating that
- a header in <code>odb/</code> or <code>odb/mysql</code> directory
- is not found, you will need to use the <code>-I</code>
- preprocessor option to specify the location of the common ODB runtime
- library (<code>libodb</code>) and MySQL ODB runtime library
- (<code>libodb-mysql</code>).</p>
-
- <p>Once the compilation is done, we can link the application with
- the following command:</p>
-
- <pre class="terminal">
-c++ -o driver driver.o person-odb.o -lodb-mysql -lodb
- </pre>
-
- <p>Notice that we link our application with two ODB libraries:
- <code>libodb</code> which is a common runtime library and
- <code>libodb-mysql</code> which is a MySQL runtime library
- (if you use another database, then the name of this library
- will change accordingly). If you get an error saying that
- one of these libraries could not be found, then you will need
- to use the <code>-L</code> linker option to specify their locations.</p>
-
- <p>Before we can run our application we need to create a database
- schema using the generated <code>person.sql</code> file. For MySQL
- we can use the <code>mysql</code> client program, for example:</p>
-
- <pre class="terminal">
-mysql --user=odb_test --database=odb_test &lt; person.sql
- </pre>
-
- <p>The above command will log in to a local MySQL server as user
- <code>odb_test</code> without a password and use the database
- named <code>odb_test</code>. Beware that after executing this
- command, all the data stored in the <code>odb_test</code> database
- will be deleted.</p>
-
- <p>Note also that using a standalone generated SQL file is not the
- only way to create a database schema in ODB. We can also embed
- the schema directly into our application or use custom schemas
- that were not generated by the ODB compiler. Refer to
- <a href="#3.4">Section 3.4, "Database"</a> for details.</p>
-
- <p>Once the database schema is ready, we run our application
- using the same login and database name:</p>
-
- <pre class="terminal">
-./driver --user odb_test --database odb_test
- </pre>
-
-
- <h2><a name="2.4">2.4 Making Objects Persistent</a></h2>
-
- <p>Now that we have the infrastructure work out of the way, it
- is time to see our first code fragment that interacts with the
- database. In this section we will learn how to make <code>person</code>
- objects persistent:</p>
-
- <pre class="cxx">
-// driver.cxx
-//
-
-#include &lt;memory> // std::auto_ptr
-#include &lt;iostream>
-
-#include &lt;odb/database.hxx>
-#include &lt;odb/transaction.hxx>
-
-#include &lt;odb/mysql/database.hxx>
-
-#include "person.hxx"
-#include "person-odb.hxx"
-
-using namespace std;
-using namespace odb::core;
-
-int
-main (int argc, char* argv[])
-{
- try
- {
- auto_ptr&lt;database> db (new odb::mysql::database (argc, argv));
-
- unsigned long john_id, jane_id, joe_id;
-
- // Create a few persistent person objects.
- //
- {
- person john ("John", "Doe", 33);
- person jane ("Jane", "Doe", 32);
- person joe ("Joe", "Dirt", 30);
-
- transaction t (db->begin ());
-
- // Make objects persistent and save their ids for later use.
- //
- john_id = db->persist (john);
- jane_id = db->persist (jane);
- joe_id = db->persist (joe);
-
- t.commit ();
- }
- }
- catch (const odb::exception&amp; e)
- {
- cerr &lt;&lt; e.what () &lt;&lt; endl;
- return 1;
- }
-}
- </pre>
-
- <p>Let's examine this code piece by piece. At the beginning we include
- a bunch of headers. After the standard C++ headers we include
- <code>&lt;odb/database.hxx></code>
- and <code>&lt;odb/transaction.hxx></code> which define database
- system-independent <code>odb::database</code> and
- <code>odb::transaction</code> interfaces. Then we include
- <code>&lt;odb/mysql/database.hxx></code> which defines the
- MySQL implementation of the <code>database</code> interface. Finally,
- we include <code>person.hxx</code> and <code>person-odb.hxx</code>
- which define our persistent <code>person</code> class.</p>
-
- <p>Then we have two <code>using namespace</code> directives. The first
- one brings in the names from the standard namespace and the second
- brings in the ODB declarations which we will use later in the file.
- Notice that in the second directive we use the <code>odb::core</code>
- namespace instead of just <code>odb</code>. The former only brings
- into the current namespace the essential ODB names, such as the
- <code>database</code> and <code>transaction</code> classes, without
- any of the auxiliary objects. This minimizes the likelihood of name
- conflicts with other libraries. Note also that you should continue
- using the <code>odb</code> namespace when qualifying individual names.
- For example, you should write <code>odb::database</code>, not
- <code>odb::core::database</code>.</p>
-
- <p>Once we are in <code>main()</code>, the first thing we do is create
- the MySQL database object. Notice that this is the last line in
- <code>driver.cxx</code> that mentions MySQL explicitly; the rest
- of the code works through the common interfaces and is database
- system-independent. We use the <code>argc</code>/<code>argv</code>
- <code>mysql::database</code> constructor which automatically
- extract the database parameters, such as login name, password,
- database name, etc., from the command line. In your own applications
- you may prefer to use other <code>mysql::database</code>
- constructors which allow you to pass this information directly
- (<a href="#17.2">Section 17.2, "MySQL Database Class"</a>).</p>
-
- <p>Next, we create three <code>person</code> objects. Right now they are
- transient objects, which means that if we terminate the application
- at this point, they will be gone without any evidence of them ever
- existing. The next line starts a database transaction. We discuss
- transactions in detail later in this manual. For now, all we need
- to know is that all ODB database operations must be performed within
- a transaction and that a transaction is an atomic unit of work; all
- database operations performed within a transaction either succeed
- (committed) together or are automatically undone (rolled back).</p>
-
- <p>Once we are in a transaction, we call the <code>persist()</code>
- database function on each of our <code>person</code> objects.
- At this point the state of each object is saved in the database.
- However, note that this state is not permanent until and unless
- the transaction is committed. If, for example, our application
- crashes at this point, there will still be no evidence of our
- objects ever existing.</p>
-
- <p>In our case, one more thing happens when we call <code>persist()</code>.
- Remember that we decided to use database-assigned identifiers for our
- <code>person</code> objects. The call to <code>persist()</code> is
- where this assignment happens. Once this function returns, the
- <code>id_</code> member contains this object's unique identifier.
- As a convenience, the <code>persist()</code> function also returns
- a copy of the object's identifier that it made persistent. We
- save the returned identifier for each object in a local variable.
- We will use these identifiers later in the chapter to perform other
- database operations on our persistent objects.</p>
-
- <p>After we have persisted our objects, it is time to commit the
- transaction and make the changes permanent. Only after the
- <code>commit()</code> function returns successfully, are we
- guaranteed that the objects are made persistent. Continuing
- with the crash example, if our application terminates after
- the commit for whatever reason, the objects' state in the
- database will remain intact. In fact, as we will discover
- shortly, our application can be restarted and load the
- original objects from the database. Note also that a
- transaction must be committed explicitly with the
- <code>commit()</code> call. If the <code>transaction</code>
- object leaves scope without the transaction being
- explicitly committed or rolled back, it will automatically be
- rolled back. This behavior allows you not to worry about
- exceptions being thrown within a transaction; if they
- cross the transaction boundary, the transaction will
- automatically be rolled back and all the changes made
- to the database undone.</p>
-
- <p>The final bit of code in our example is the <code>catch</code>
- block that handles the database exceptions. We do this by catching
- the base ODB exception (<a href="#3.14">Section 3.14, "ODB
- Exceptions"</a>) and printing the diagnostics.</p>
-
- <p>Let's now compile (<a href="#2.3">Section 2.3, "Compiling and
- Running"</a>) and then run our first ODB application:</p>
-
- <pre class="terminal">
-mysql --user=odb_test --database=odb_test &lt; person.sql
-./driver --user odb_test --database odb_test
- </pre>
-
- <p>Our first application doesn't print anything except for error
- messages so we can't really tell whether it actually stored the
- objects' state in the database. While we will make our application
- more entertaining shortly, for now we can use the <code>mysql</code>
- client to examine the database content. It will also give us a feel
- for how the objects are stored:</p>
-
- <pre class="terminal">
-mysql --user=odb_test --database=odb_test
-
-Welcome to the MySQL monitor.
-
-mysql> select * from person;
-
-+----+-------+------+-----+
-| id | first | last | age |
-+----+-------+------+-----+
-| 1 | John | Doe | 33 |
-| 2 | Jane | Doe | 32 |
-| 3 | Joe | Dirt | 30 |
-+----+-------+------+-----+
-3 rows in set (0.00 sec)
-
-mysql> quit
- </pre>
-
- <p>Another way to get more insight into what's going on under the hood,
- is to trace the SQL statements executed by ODB as a result of
- each database operation. Here is how we can enable tracing just for
- the duration of our transaction:</p>
-
- <pre class="cxx">
- // Create a few persistent person objects.
- //
- {
- ...
-
- transaction t (db->begin ());
-
- t.tracer (stderr_tracer);
-
- // Make objects persistent and save their ids for later use.
- //
- john_id = db->persist (john);
- jane_id = db->persist (jane);
- joe_id = db->persist (joe);
-
- t.commit ();
- }
- </pre>
-
- <p>With this modification our application now produces the following
- output:</p>
-
- <pre class="terminal">
-INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?)
-INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?)
-INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?)
- </pre>
-
- <p>Note that we see question marks instead of the actual values
- because ODB uses prepared statements and sends the data to the
- database in binary form. For more information on tracing, refer
- to <a href="#3.13">Section 3.13, "Tracing SQL Statement Execution"</a>.
- In the next section we will see how to access persistent objects
- from our application.</p>
-
- <h2><a name="2.5">2.5 Querying the Database for Objects</a></h2>
-
- <p>So far our application doesn't resemble a typical "Hello World"
- example. It doesn't print anything except for error messages.
- Let's change that and teach our application to say hello to
- people from our database. To make it a bit more interesting,
- let's say hello only to people over 30:</p>
-
- <pre class="cxx">
-// driver.cxx
-//
-
-...
-
-int
-main (int argc, char* argv[])
-{
- try
- {
- ...
-
- // Create a few persistent person objects.
- //
- {
- ...
- }
-
- typedef odb::query&lt;person> query;
- typedef odb::result&lt;person> result;
-
- // Say hello to those over 30.
- //
- {
- transaction t (db->begin ());
-
- result r (db->query&lt;person> (query::age > 30));
-
- for (result::iterator i (r.begin ()); i != r.end (); ++i)
- {
- cout &lt;&lt; "Hello, " &lt;&lt; i->first () &lt;&lt; "!" &lt;&lt; endl;
- }
-
- t.commit ();
- }
- }
- catch (const odb::exception&amp; e)
- {
- cerr &lt;&lt; e.what () &lt;&lt; endl;
- return 1;
- }
-}
- </pre>
-
- <p>The first half of our application is the same as before and is
- replaced with "..." in the above listing for brevity. Again, let's
- examine the rest of it piece by piece.</p>
-
- <p>The two <code>typedef</code>s create convenient aliases for two
- template instantiations that will be used a lot in our application.
- The first is the query type for the <code>person</code> objects
- and the second is the result type for that query.</p>
-
- <p>Then we begin a new transaction and call the <code>query()</code>
- database function. We pass a query expression
- (<code>query::age > 30</code>) which limits the returned objects
- only to those with the age greater than 30. We also save the result
- of the query in a local variable.</p>
-
- <p>The next few lines perform a standard for-loop iteration
- over the result sequence printing hello for every returned person.
- Then we commit the transaction and that's it. Let's see what
- this application will print:</p>
-
- <pre class="terminal">
-mysql --user=odb_test --database=odb_test &lt; person.sql
-./driver --user odb_test --database odb_test
-
-Hello, John!
-Hello, Jane!
- </pre>
-
-
- <p>That looks about right, but how do we know that the query actually
- used the database instead of just using some in-memory artifacts of
- the earlier <code>persist()</code> calls? One way to test this
- would be to comment out the first transaction in our application
- and re-run it without re-creating the database schema. This way the
- objects that were persisted during the previous run will be returned.
- Alternatively, we can just re-run the same application without
- re-creating the schema and notice that we now show duplicate
- objects:</p>
-
- <pre class="terminal">
-./driver --user odb_test --database odb_test
-
-Hello, John!
-Hello, Jane!
-Hello, John!
-Hello, Jane!
- </pre>
-
- <p>What happens here is that the previous run of our application
- persisted a set of <code>person</code> objects and when we re-run
- the application, we persist another set with the same names but
- with different ids. When we later run the query, matches from
- both sets are returned. We can change the line where we print
- the "Hello" string as follows to illustrate this point:</p>
-
- <pre class="cxx">
-cout &lt;&lt; "Hello, " &lt;&lt; i->first () &lt;&lt; " (" &lt;&lt; i->id () &lt;&lt; ")!" &lt;&lt; endl;
- </pre>
-
- <p>If we now re-run this modified program, again without re-creating
- the database schema, we will get the following output:</p>
-
- <pre class="terminal">
-./driver --user odb_test --database odb_test
-
-Hello, John (1)!
-Hello, Jane (2)!
-Hello, John (4)!
-Hello, Jane (5)!
-Hello, John (7)!
-Hello, Jane (8)!
- </pre>
-
- <p>The identifiers 3, 6, and 9 that are missing from the above list belong
- to the "Joe Dirt" objects which are not selected by this query.</p>
-
- <h2><a name="2.6">2.6 Updating Persistent Objects</a></h2>
-
- <p>While making objects persistent and then selecting some of them using
- queries are two useful operations, most applications will also need
- to change the object's state and then make these changes persistent.
- Let's illustrate this by updating Joe's age who just had a birthday:</p>
-
- <pre class="cxx">
-// driver.cxx
-//
-
-...
-
-int
-main (int argc, char* argv[])
-{
- try
- {
- ...
-
- unsigned long john_id, jane_id, joe_id;
-
- // Create a few persistent person objects.
- //
- {
- ...
-
- // Save object ids for later use.
- //
- john_id = john.id ();
- jane_id = jane.id ();
- joe_id = joe.id ();
- }
-
- // Joe Dirt just had a birthday, so update his age.
- //
- {
- transaction t (db->begin ());
-
- auto_ptr&lt;person> joe (db->load&lt;person> (joe_id));
- joe->age (joe->age () + 1);
- db->update (*joe);
-
- t.commit ();
- }
-
- // Say hello to those over 30.
- //
- {
- ...
- }
- }
- catch (const odb::exception&amp; e)
- {
- cerr &lt;&lt; e.what () &lt;&lt; endl;
- return 1;
- }
-}
- </pre>
-
- <p>The beginning and the end of the new transaction are the same as
- the previous two. Once within a transaction, we call the
- <code>load()</code> database function to instantiate a
- <code>person</code> object with Joe's persistent state. We
- pass Joe's object identifier that we stored earlier when we
- made this object persistent. While here we use
- <code>std::auto_ptr</code> to manage the returned object, we
- could have also used another smart pointer, for example
- <code>std::unique_ptr</code> from C++11 or <code>shared_ptr</code>
- from TR1, C++11, or Boost. For more information
- on the object lifetime management and the smart pointers that we
- can use for that, see <a href="#3.3">Section 3.3, "Object
- and View Pointers"</a>.</p>
-
- <p>With the instantiated object in hand we increment the age
- and call the <code>update()</code> function to update
- the object's state in the database. Once the transaction is
- committed, the changes are made permanent.</p>
-
- <p>If we now run this application, we will see Joe in the output
- since he is now over 30:</p>
-
- <pre class="terminal">
-mysql --user=odb_test --database=odb_test &lt; person.sql
-./driver --user odb_test --database odb_test
-
-Hello, John!
-Hello, Jane!
-Hello, Joe!
- </pre>
-
- <p>What if we didn't have an identifier for Joe? Maybe this object
- was made persistent in another run of our application or by another
- application altogether. Provided that we only have one Joe Dirt
- in the database, we can use the query facility to come up with
- an alternative implementation of the above transaction:</p>
-
- <pre class="cxx">
- // Joe Dirt just had a birthday, so update his age. An
- // alternative implementation without using the object id.
- //
- {
- transaction t (db->begin ());
-
- // Here we know that there can be only one Joe Dirt in our
- // database so we use the query_one() shortcut instead of
- // manually iterating over the result returned by query().
- //
- auto_ptr&lt;person> joe (
- db->query_one&lt;person> (query::first == "Joe" &amp;&amp;
- query::last == "Dirt"));
-
- if (joe.get () != 0)
- {
- joe->age (joe->age () + 1);
- db->update (*joe);
- }
-
- t.commit ();
- }
- </pre>
-
- <h2><a name="2.7">2.7 Defining and Using Views</a></h2>
-
- <p>Suppose that we need to gather some basic statistics about the people
- stored in our database. Things like the total head count, as well as
- the minimum and maximum ages. One way to do it would be to query
- the database for all the <code>person</code> objects and then
- calculate this information as we iterate over the query result.
- While this approach may work fine for our database with just three
- people in it, it would be very inefficient if we had a large
- number of objects.</p>
-
- <p>While it may not be conceptually pure from the object-oriented
- programming point of view, a relational database can perform
- some computations much faster and much more economically than
- if we performed the same operations ourselves in the application's
- process.</p>
-
- <p>To support such cases ODB provides the notion of views. An ODB view
- is a C++ <code>class</code> that embodies a light-weight, read-only
- projection of one or more persistent objects or database tables or
- the result of a native SQL query execution or stored procedure
- call.</p>
-
- <p>Some of the common applications of views include loading a subset of
- data members from objects or columns database tables, executing and
- handling results of arbitrary SQL queries, including aggregate
- queries, as well as joining multiple objects and/or database
- tables using object relationships or custom join conditions.</p>
-
- <p>While you can find a much more detailed description of views in
- <a href="#10">Chapter 10, "Views"</a>, here is how we can define
- the <code>person_stat</code> view that returns the basic statistics
- about the <code>person</code> objects:</p>
-
- <pre class="cxx">
-#pragma db view object(person)
-struct person_stat
-{
- #pragma db column("count(" + person::id_ + ")")
- std::size_t count;
-
- #pragma db column("min(" + person::age_ + ")")
- unsigned short min_age;
-
- #pragma db column("max(" + person::age_ + ")")
- unsigned short max_age;
-};
- </pre>
-
- <p>Normally, to get the result of a view we use the same
- <code>query()</code> function as when querying the database for
- an object. Here, however, we are executing an aggregate query
- which always returns exactly one element. Therefore, instead
- of getting the result instance and then iterating over it, we
- can use the shortcut <code>query_value()</code> function. Here is
- how we can load and print our statistics using the view we have
- just created:</p>
-
- <pre class="cxx">
- // Print some statistics about all the people in our database.
- //
- {
- transaction t (db->begin ());
-
- // The result of this query always has exactly one element.
- //
- person_stat ps (db->query_value&lt;person_stat> ());
-
- cout &lt;&lt; "count : " &lt;&lt; ps.count &lt;&lt; endl
- &lt;&lt; "min age: " &lt;&lt; ps.min_age &lt;&lt; endl
- &lt;&lt; "max age: " &lt;&lt; ps.max_age &lt;&lt; endl;
-
- t.commit ();
- }
- </pre>
-
- <p>If we now add the <code>person_stat</code> view to the
- <code>person.hxx</code> header, the above transaction
- to <code>driver.cxx</code>, as well as re-compile and
- re-run our example, then we will see the following
- additional lines in the output:</p>
-
- <pre class="term">
-count : 3
-min age: 31
-max age: 33
- </pre>
-
- <h2><a name="2.8">2.8 Deleting Persistent Objects</a></h2>
-
- <p>The last operation that we will discuss in this chapter is deleting
- the persistent object from the database. The following code
- fragment shows how we can delete an object given its identifier:</p>
-
- <pre class="cxx">
- // John Doe is no longer in our database.
- //
- {
- transaction t (db->begin ());
- db->erase&lt;person> (john_id);
- t.commit ();
- }
- </pre>
-
- <p>To delete John from the database we start a transaction, call
- the <code>erase()</code> database function with John's object
- id, and commit the transaction. After the transaction is committed,
- the erased object is no longer persistent.</p>
-
- <p>If we don't have an object id handy, we can use queries to find
- and delete the object:</p>
-
- <pre class="cxx">
- // John Doe is no longer in our database. An alternative
- // implementation without using the object id.
- //
- {
- transaction t (db->begin ());
-
- // Here we know that there can be only one John Doe in our
- // database so we use the query_one() shortcut again.
- //
- auto_ptr&lt;person> john (
- db->query_one&lt;person> (query::first == "John" &amp;&amp;
- query::last == "Doe"));
-
- if (john.get () != 0)
- db->erase (*john);
-
- t.commit ();
- }
- </pre>
-
- <h2><a name="2.9">2.9 Changing Persistent Classes</a></h2>
-
- <p>When the definition of a transient C++ class is changed, for
- example by adding or deleting a data member, we don't have to
- worry about any existing instances of this class not matching
- the new definition. After all, to make the class changes
- effective we have to restart the application and none of the
- transient instances will survive this.</p>
-
- <p>Things are not as simple for persistent classes. Because they
- are stored in the database and therefore survive application
- restarts, we have a new problem: what happens to the state of
- existing objects (which correspond to the old definition) once
- we change our persistent class?</p>
-
- <p>The problem of working with old objects, called <em>database
- schema evolution</em>, is a complex issue and ODB provides
- comprehensive support for handling it. While this support
- is covered in detail in <a href="#13">Chapter 13,
- "Database Schema Evolution"</a>, let us consider a simple
- example that should give us a sense of the functionality
- provided by ODB in this area.</p>
-
- <p>Suppose that after using our <code>person</code> persistent
- class for some time and creating a number of databases
- containing its instances, we realized that for some people
- we also need to store their middle name. If we go ahead and
- just add the new data member, everything will work fine
- with new databases. Existing databases, however, have a
- table that does not correspond to the new class definition.
- Specifically, the generated database support code now
- expects there to be a column to store the middle name.
- But such a column was never created in the old databases.</p>
-
- <p>ODB can automatically generate SQL statements that will
- migrate old databases to match the new class definitions.
- But first, we need to enable schema evolution support by
- defining a version for our object model:</p>
-
- <pre class="cxx">
-// person.hxx
-//
-
-#pragma db model version(1, 1)
-
-class person
-{
- ...
-
- std::string first_;
- std::string last_;
- unsigned short age_;
-};
- </pre>
-
- <p>The first number in the <code>version</code> pragma is the
- base model version. This is the lowest version we will be
- able to migrate from. The second number is the current model
- version. Since we haven't made any changes yet to our
- persistent class, both of these values are <code>1</code>.</p>
-
- <p>Next we need to re-compile our <code>person.hxx</code> header
- file with the ODB compiler, just as we did before:</p>
-
- <pre class="terminal">
-odb -d mysql --generate-query --generate-schema person.hxx
- </pre>
-
- <p>If we now look at the list of files produced by the ODB compiler,
- we will notice a new file: <code>person.xml</code>. This
- is a changelog file where the ODB compiler keeps track of the
- database changes corresponding to our class changes. Note that
- this file is automatically maintained by the ODB compiler and
- all we have to do is keep it around between re-compilations.</p>
-
- <p>Now we are ready to add the middle name to our <code>person</code>
- class. We also give it a default value (empty string) which
- is what will be assigned to existing objects in old databases.
- Notice that we have also incremented the current version:</p>
-
- <pre class="cxx">
-// person.hxx
-//
-
-#pragma db model version(1, 2)
-
-class person
-{
- ...
-
- std::string first_;
-
- #pragma db default("")
- std::string middle_;
-
- std::string last_;
- unsigned short age_;
-};
- </pre>
-
- <p>If we now recompile the <code>person.hxx</code> header again, we will
- see two extra generated files: <code>person-002-pre.sql</code>
- and <code>person-002-post.sql</code>. These two files contain
- schema migration statements from version <code>1</code> to
- version <code>2</code>. Similar to schema creation, schema
- migration statements can also be embedded into the generated
- C++ code.</p>
-
- <p><code>person-002-pre.sql</code> and <code>person-002-post.sql</code>
- are the pre and post schema migration files. To migrate
- one of our old databases, we first execute the pre migration
- file:</p>
-
- <pre class="terminal">
-mysql --user=odb_test --database=odb_test &lt; person-002-pre.sql
- </pre>
-
- <p>Between the pre and post schema migrations we can run data
- migration code, if required. At this stage, we can both
- access the old and store the new data. In our case we don't
- need any data migration code since we assigned the default
- value to the middle name for all the existing objects.</p>
-
- <p>To finish the migration process we execute the post migration
- statements:</p>
-
- <pre class="terminal">
-mysql --user=odb_test --database=odb_test &lt; person-002-post.sql
- </pre>
-
- <h2><a name="2.10">2.10 Working with Multiple Databases</a></h2>
-
- <p>Accessing multiple databases (that is, data stores) is simply a
- matter of creating multiple <code>odb::&lt;db>::database</code>
- instances representing each database. For example:</p>
-
- <pre class="cxx">
-odb::mysql::database db1 ("john", "secret", "test_db1");
-odb::mysql::database db2 ("john", "secret", "test_db2");
- </pre>
-
- <p>Some database systems also allow attaching multiple databases to
- the same instance. A more interesting question is how we access
- multiple database systems (that is, database implementations) from
- the same application. For example, our application may need to store
- some objects in a remote MySQL database and others in a local SQLite
- file. Or, our application may need to be able to store its objects
- in a database system that is selected by the user at runtime.</p>
-
- <p>ODB provides comprehensive multi-database support that ranges from
- tight integration with specific database systems to being able to
- write database-agnostic code and loading individual database systems
- support dynamically. While all these aspects are covered in detail
- in <a href="#16">Chapter 16, "Multi-Database Support"</a>, in this
- section we will get a taste of this functionality by extending our
- "Hello World" example to be able to store its data either in MySQL
- or PostgreSQL (other database systems supported by ODB can be added
- in a similar manner).</p>
-
- <p>The first step in adding multi-database support is to re-compile
- our <code>person.hxx</code> header to generate database support
- code for additional database systems:</p>
-
- <pre class="terminal">
-odb --multi-database dynamic -d common -d mysql -d pgsql \
---generate-query --generate-schema person.hxx
- </pre>
-
- <p>The <code>--multi-database</code> ODB compiler option turns on
- multi-database support. For now it is not important what the
- <code>dynamic</code> value that we passed to this option means, but
- if you are curious, see <a href="#16">Chapter 16</a>. The result of this
- command are three sets of generated files: <code>person-odb.?xx</code>
- (common interface; corresponds to the <code>common</code> database),
- <code>person-odb-mysql.?xx</code> (MySQL support code), and
- <code>person-odb-pgsql.?xx</code> (PostgreSQL support code). There
- are also two schema files: <code>person-mysql.sql</code> and
- <code>person-pgsql.sql</code>.</p>
-
- <p>The only part that we need to change in <code>driver.cxx</code>
- is how we create the database instance. Specifically, this line:</p>
-
- <pre class="cxx">
-auto_ptr&lt;database> db (new odb::mysql::database (argc, argv));
- </pre>
-
- <p>Now our example is capable of storing its data either in MySQL or
- PostgreSQL so we need to somehow allow the caller to specify which
- database we must use. To keep things simple, we will make the first
- command line argument specify the database system we must use while
- the rest will contain the database-specific options which we will
- pass to the <code>odb::&lt;db>::database</code> constructor as
- before. Let's put all this logic into a separate function which we
- will call <code>create_database()</code>. Here is what the beginning
- of our modified <code>driver.cxx</code> will look like (the remainder
- is unchanged):</p>
-
- <pre class="cxx">
-// driver.cxx
-//
-
-#include &lt;string>
-#include &lt;memory> // std::auto_ptr
-#include &lt;iostream>
-
-#include &lt;odb/database.hxx>
-#include &lt;odb/transaction.hxx>
-
-#include &lt;odb/mysql/database.hxx>
-#include &lt;odb/pgsql/database.hxx>
-
-#include "person.hxx"
-#include "person-odb.hxx"
-
-using namespace std;
-using namespace odb::core;
-
-auto_ptr&lt;database>
-create_database (int argc, char* argv[])
-{
- auto_ptr&lt;database> r;
-
- if (argc &lt; 2)
- {
- cerr &lt;&lt; "error: database system name expected" &lt;&lt; endl;
- return r;
- }
-
- string db (argv[1]);
-
- if (db == "mysql")
- r.reset (new odb::mysql::database (argc, argv));
- else if (db == "pgsql")
- r.reset (new odb::pgsql::database (argc, argv));
- else
- cerr &lt;&lt; "error: unknown database system " &lt;&lt; db &lt;&lt; endl;
-
- return r;
-}
-
-int
-main (int argc, char* argv[])
-{
- try
- {
- auto_ptr&lt;database> db (create_database (argc, argv));
-
- if (db.get () == 0)
- return 1; // Diagnostics has already been issued.
-
- ...
- </pre>
-
- <p>And that's it. The only thing left is to build and run our
- example:</p>
-
- <pre class="terminal">
-c++ -c driver.cxx
-c++ -c person-odb.cxx
-c++ -c person-odb-mysql.cxx
-c++ -c person-odb-pgsql.cxx
-c++ -o driver driver.o person-odb.o person-odb-mysql.o \
-person-odb-pgsql.o -lodb-mysql -lodb-pgsql -lodb
- </pre>
-
- <p>Here is how we can access a MySQL database:</p>
-
- <pre class="terminal">
-mysql --user=odb_test --database=odb_test &lt; person-mysql.sql
-./driver mysql --user odb_test --database odb_test
- </pre>
-
- <p>Or a PostgreSQL database:</p>
-
- <pre class="terminal">
-psql --user=odb_test --dbname=odb_test -f person-pgsql.sql
-./driver pgsql --user odb_test --database odb_test
- </pre>
-
- <h2><a name="2.11">2.11 Summary</a></h2>
-
- <p>This chapter presented a very simple application which, nevertheless,
- exercised all of the core database functions: <code>persist()</code>,
- <code>query()</code>, <code>load()</code>, <code>update()</code>,
- and <code>erase()</code>. We also saw that writing an application
- that uses ODB involves the following steps:</p>
-
- <ol>
- <li>Declare persistent classes in header files.</li>
- <li>Compile these headers to generate database support code.</li>
- <li>Link the application with the generated code and two ODB runtime
- libraries.</li>
- </ol>
-
- <p>Do not be concerned if, at this point, much appears unclear. The intent
- of this chapter is to give you only a general idea of how to persist C++
- objects with ODB. We will cover all the details throughout the remainder
- of this manual.</p>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="3">3 Working with Persistent Objects</a></h1>
-
- <p>The previous chapters gave us a high-level overview of ODB and
- showed how to use it to store C++ objects in a database. In this
- chapter we will examine the ODB object persistence model as
- well as the core database APIs in greater detail. We will
- start with basic concepts and terminology in <a href="#3.1">Section
- 3.1</a> and <a href="#3.3">Section 3.3</a> and continue with the
- discussion of the <code>odb::database</code> class in
- <a href="#3.4">Section 3.4</a>, transactions in
- <a href="#3.5">Section 3.5</a>, and connections in
- <a href="#3.6">Section 3.6</a>. The remainder of this chapter
- deals with the core database operations and concludes with
- the discussion of ODB exceptions.</p>
-
- <p>In this chapter we will continue to use and expand the
- <code>person</code> persistent class that we have developed in the
- previous chapter.</p>
-
- <h2><a name="3.1">3.1 Concepts and Terminology</a></h2>
-
- <p>The term <em>database</em> can refer to three distinct things:
- a general notion of a place where an application stores its data,
- a software implementation for managing this data (for example
- MySQL), and, finally, some database software implementations
- may manage several data stores which are usually distinguished
- by name. This name is also commonly referred to as a database.</p>
-
- <p>In this manual, when we use the word <em>database</em>, we
- refer to the first meaning above, for example,
- "The <code>update()</code> function saves the object's state to
- the database." The term Database Management System (DBMS) is
- often used to refer to the second meaning of the word database.
- In this manual we will use the term <em>database system</em>
- for short, for example, "Database system-independent
- application code." Finally, to distinguish the third meaning
- from the other two, we will use the term <em>database name</em>,
- for example, "The second option specifies the database name
- that the application should use to store its data."</p>
-
- <p>In C++ there is only one notion of a type and an instance
- of a type. For example, a fundamental type, such as <code>int</code>,
- is, for the most part, treated the same as a user defined class
- type. However, when it comes to persistence, we have to place
- certain restrictions and requirements on certain C++ types that
- can be stored in the database. As a result, we divide persistent
- C++ types into two groups: <em>object types</em> and <em>value
- types</em>. An instance of an object type is called an <em>object</em>
- and an instance of a value type &mdash; a <em>value</em>.</p>
-
- <p>An object is an independent entity. It can be stored, updated,
- and deleted in the database independent of other objects.
- Normally, an object has an identifier, called <em>object id</em>,
- that is unique among all instances of an object type within a
- database. In contrast, a value can only be stored in the database
- as part of an object and doesn't have its own unique identifier.</p>
-
- <p>An object consists of data members which are either values
- (<a href="#7">Chapter 7, "Value Types"</a>), pointers
- to other objects (<a href="#6">Chapter 6, "Relationships"</a>), or
- containers of values or pointers to other objects (<a href="#5">Chapter
- 5, "Containers")</a>. Pointers to other objects and containers can
- be viewed as special kinds of values since they also can only
- be stored in the database as part of an object. Static data members
- are not stored in the database.</p>
-
- <p>An object type is a C++ class. Because of this one-to-one
- relationship, we will use terms <em>object type</em>
- and <em>object class</em> interchangeably. In contrast,
- a value type can be a fundamental C++ type, such as
- <code>int</code> or a class type, such as <code>std::string</code>.
- If a value consists of other values, then it is called a
- <em>composite value</em> and its type &mdash; a
- <em>composite value type</em> (<a href="#7.2">Section 7.2,
- "Composite Value Types"</a>). Otherwise, the value is
- called <em>simple value</em> and its type &mdash; a
- <em>simple value type</em> (<a href="#7.1">Section 7.1,
- "Simple Value Types"</a>). Note that the distinction between
- simple and composite values is conceptual rather than
- representational. For example, <code>std::string</code>
- is a simple value type because conceptually string is a
- single value even though the representation of the string
- class may contain several data members each of which could be
- considered a value. In fact, the same value type can be
- viewed (and mapped) as both simple and composite by different
- applications.</p>
-
- <p>While not strictly necessary in a purely object-oriented application,
- practical considerations often require us to only load a
- subset of an object's data members or a combination of members
- from several objects. We may also need to factor out some
- computations to the relational database instead of performing
- them in the application's process. To support such requirements
- ODB distinguishes a third kind of C++ types, called <em>views</em>
- (<a href="#10">Chapter 10, "Views"</a>). An ODB view is a C++
- <code>class</code> that embodies a light-weight, read-only
- projection of one or more persistent objects or database
- tables or the result of a native SQL query execution.</p>
-
- <p>Understanding how all these concepts map to the relational model
- will hopefully make these distinctions clearer. In a relational
- database an object type is mapped to a table and a value type is
- mapped to one or more columns. A simple value type is mapped
- to a single column while a composite value type is mapped to
- several columns. An object is stored as a row in this
- table and a value is stored as one or more cells in this row.
- A simple value is stored in a single cell while a composite
- value occupies several cells. A view is not a persistent
- entity and it is not stored in the database. Rather, it is a
- data structure that is used to capture a single row of an SQL
- query result.</p>
-
- <p>Going back to the distinction between simple and composite
- values, consider a date type which has three integer
- members: year, month, and day. In one application it can be
- considered a composite value and each member will get its
- own column in a relational database. In another application
- it can be considered a simple value and stored in a single
- column as a number of days from some predefined date.</p>
-
- <p>Until now, we have been using the term <em>persistent class</em>
- to refer to object classes. We will continue to do so even though
- a value type can also be a class. The reason for this asymmetry
- is the subordinate nature of value types when it comes to
- database operations. Remember that values are never stored
- directly but rather as part of an object that contains them.
- As a result, when we say that we want to make a C++ class
- persistent or persist an instance of a class in the database,
- we invariably refer to an object class rather than a value
- class.</p>
-
- <p>Normally, you would use object types to model real-world entities,
- things that have their own identity. For example, in the
- previous chapter we created a <code>person</code> class to model
- a person, which is a real-world entity. Name and age, which we
- used as data members in our <code>person</code> class are clearly
- values. It is hard to think of age 31 or name "Joe" as having their
- own identities.</p>
-
- <p>A good test to determine whether something is an object or
- a value, is to consider if other objects might reference
- it. A person is clearly an object because it can be referred
- to by other objects such as a spouse, an employer, or a
- bank. On the other hand, a person's age or name is not
- something that other objects would normally refer to.</p>
-
- <p>Also, when an object represents a real entity, it is easy to
- choose a suitable object id. For example, for a
- person there is an established notion of an identifier
- (SSN, student id, passport number, etc). Another alternative
- is to use a person's email address as an identifier.</p>
-
- <p>Note, however, that these are only guidelines. There could
- be good reasons to make something that would normally be
- a value an object. Consider, for example, a database that
- stores a vast number of people. Many of the <code>person</code>
- objects in this database have the same names and surnames and
- the overhead of storing them in every object may negatively
- affect the performance. In this case, we could make the first name
- and last name each an object and only store pointers to
- these objects in the <code>person</code> class.</p>
-
- <p>An instance of a persistent class can be in one of two states:
- <em>transient</em> and <em>persistent</em>. A transient
- instance only has a representation in the application's
- memory and will cease to exist when the application terminates,
- unless it is explicitly made persistent. In other words, a
- transient instance of a persistent class behaves just like an
- instance of any ordinary C++ class. A persistent instance
- has a representation in both the application's memory and the
- database. A persistent instance will remain even after the
- application terminates unless and until it is explicitly
- deleted from the database.</p>
-
- <h2><a name="3.2">3.2 Declaring Persistent Objects and Values</a></h2>
-
- <p>To make a C++ class a persistent object class we declare
- it as such using the <code>db&nbsp;object</code> pragma, for
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-};
- </pre>
-
- <p>The other pragma that we often use is <code>db&nbsp;id</code>
- which designates one of the data members as an object id, for
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db id
- unsigned long id_;
-};
- </pre>
-
- <p>The object id can be of a simple or composite (<a href="#7.2.1">Section
- 7.2.1, "Composite Object Ids"</a>) value type. This type should be
- default-constructible, copy-constructible, and copy-assignable. It
- is also possible to declare a persistent class without an object id,
- however, such a class will have limited functionality
- (<a href="#14.1.6">Section 14.1.6, "<code>no_id</code>"</a>).</p>
-
- <p>The above two pragmas are the minimum required to declare a
- persistent class with an object id. Other pragmas can be used to
- fine-tune the database-related properties of a class and its
- members (<a href="#14">Chapter 14, "ODB Pragma Language"</a>).</p>
-
- <p>Normally, a persistent class should define the default constructor. The
- generated database support code uses this constructor when
- instantiating an object from the persistent state. If we add the
- default constructor only for the database support code, then we
- can make it private provided we also make the <code>odb::access</code>
- class, defined in the <code>&lt;odb/core.hxx></code> header, a
- friend of this object class. For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/core.hxx>
-
-#pragma db object
-class person
-{
- ...
-
-private:
- friend class odb::access;
- person () {}
-};
- </pre>
-
- <p>It is also possible to have an object class without the default
- constructor. However, in this case, the database operations will
- only be able to load the persistent state into an existing instance
- (<a href="#3.9">Section 3.9, "Loading Persistent Objects"</a>,
- <a href="#4.4">Section 4.4, "Query Result"</a>).</p>
-
- <p>The ODB compiler also needs access to the non-transient
- (<a href="#14.4.11">Section 14.4.11, "<code>transient</code>"</a>)
- data members of a persistent class. The ODB compiler can access
- such data members directly if they are public. It can also do
- so if they are private or protected and the <code>odb::access</code>
- class is declared a friend of the object type. For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/core.hxx>
-
-#pragma db object
-class person
-{
- ...
-
-private:
- friend class odb::access;
- person () {}
-
- #pragma db id
- unsigned long id_;
-
- std::string name_;
-};
- </pre>
-
- <p>If data members are not accessible directly, then the ODB
- compiler will try to automatically find suitable accessor and
- modifier functions. To accomplish this, the ODB compiler will
- try to lookup common accessor and modifier names derived from
- the data member name. Specifically, for the <code>name_</code>
- data member in the above example, the ODB compiler will look
- for accessor functions with names: <code>get_name()</code>,
- <code>getName()</code>, <code>getname()</code>, and just
- <code>name()</code> as well as for modifier functions with
- names: <code>set_name()</code>, <code>setName()</code>,
- <code>setname()</code>, and just <code>name()</code>. You can
- also add support for custom name derivations with the
- <code>--accessor-regex</code> and <code>--modifier-regex</code>
- ODB compiler options. Refer to the
- <a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
- Compiler Command Line Manual</a> for details on these options.
- The following example illustrates automatic accessor and modifier
- discovery:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
-public:
- person () {}
-
- ...
-
- unsigned long id () const;
- void id (unsigned long);
-
- const std::string&amp; get_name () const;
- std::string&amp; set_name ();
-
-private:
- #pragma db id
- unsigned long id_; // Uses id() for access.
-
- std::string name_; // Uses get_name()/set_name() for access.
-};
- </pre>
-
- <p>Finally, if a data member is not directly accessible and the
- ODB compiler was unable to discover suitable accessor and
- modifier functions, then we can provide custom accessor
- and modifier expressions using the <code>db&nbsp;get</code>
- and <code>db&nbsp;set</code> pragmas. For more information
- on custom accessor and modifier expressions refer to
- <a href="#14.4.5">Section 14.4.5,
- "<code>get</code>/<code>set</code>/<code>access</code>"</a>.</p>
-
- <p>Data members of a persistent class can also be split into
- separately-loaded and/or separately-updated sections.
- For more information on this functionality, refer to
- <a href="#9">Chapter 9, "Sections"</a>.</p>
-
- <p>You may be wondering whether we also have to declare value types
- as persistent. We don't need to do anything special for simple value
- types such as <code>int</code> or <code>std::string</code> since the
- ODB compiler knows how to map them to suitable database types and
- how to convert between the two. On the other hand, if a simple value
- is unknown to the ODB compiler then we will need to provide the
- mapping to the database type and, possibly, the code to
- convert between the two. For more information on how to achieve
- this refer to the <code>db&nbsp;type</code> pragma description
- in <a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>.</p>
-
- <p>Similar to object classes, composite value types have to be
- explicitly declared as persistent using the <code>db&nbsp;value</code>
- pragma, for example:</p>
-
- <pre class="cxx">
-#pragma db value
-class name
-{
- ...
-
- std::string first_;
- std::string last_;
-};
- </pre>
-
- <p>Note that a composite value cannot have a data member designated
- as an object id since, as we have discussed earlier, values do
- not have a notion of identity. A composite value type also doesn't
- have to define the default constructor, unless it is used as an
- element of a container. The ODB compiler uses the same mechanisms
- to access data members in composite value types as in object types.
- Composite value types are discussed in more detail in
- <a href="#7.2">Section 7.2, "Composite Value Types"</a>.</p>
-
- <h2><a name="3.3">3.3 Object and View Pointers</a></h2>
-
- <p>As we have seen in the previous chapter, some database operations
- create dynamically allocated instances of persistent classes and
- return pointers to these instances. As we will see in later chapters,
- pointers are also used to establish relationships between objects
- (<a href="#6">Chapter 6, "Relationships"</a>) as well as to cache
- persistent objects in a session (<a href="#11">Chapter 11,
- "Session"</a>). While in most cases you won't need to deal with
- pointers to views, it is possible to a obtain a dynamically allocated
- instance of a view using the <code>result_iterator::load()</code>
- function (<a href="#4.4">Section 4.4, "Query Results"</a>).</p>
-
- <p>By default, all these mechanisms use raw pointers to return
- objects and views as well as to pass and cache objects. This
- is normally sufficient for applications
- that have simple object lifetime requirements and do not use sessions
- or object relationships. In particular, a dynamically allocated object
- or view that is returned as a raw pointer from a database operation
- can be assigned to a smart pointer of our choice, for example
- <code>std::auto_ptr</code>, <code>std::unique_ptr</code> from C++11, or
- <code>shared_ptr</code> from TR1, C++11, or Boost.</p>
-
- <p>However, to avoid any possibility of a mistake, such as forgetting
- to use a smart pointer for a returned object or view, as well as to
- simplify the use of more advanced ODB functionality, such as sessions
- and bidirectional object relationships, it is recommended that you use
- smart pointers with the sharing semantics as object pointers.
- The <code>shared_ptr</code> smart pointer from TR1, C++11, or Boost
- is a good default choice. However, if sharing is not required and
- sessions are not used, then <code>std::unique_ptr</code> or
- <code>std::auto_ptr</code> can be used just as well.</p>
-
- <p>ODB provides several mechanisms for changing the object or view pointer
- type. To specify the pointer type on the per object or per view basis
- we can use the <code>db&nbsp;pointer</code> pragma, for example:</p>
-
- <pre class="cxx">
-#pragma db object pointer(std::tr1::shared_ptr)
-class person
-{
- ...
-};
- </pre>
-
- <p>We can also specify the default pointer for a group of objects or
- views at the namespace level:</p>
-
- <pre class="cxx">
-#pragma db namespace pointer(std::tr1::shared_ptr)
-namespace accounting
-{
- #pragma db object
- class employee
- {
- ...
- };
-
- #pragma db object
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>Finally, we can use the <code>--default-pointer</code> option to specify
- the default pointer for the whole file. Refer to the
- <a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
- Compiler Command Line Manual</a> for details on this option's argument.
- The typical usage is shown below:</p>
-
- <pre class="terminal">
---default-pointer std::tr1::shared_ptr
- </pre>
-
- <p>An alternative to this method with the same effect is to specify the
- default pointer for the global namespace:</p>
-
- <pre class="terminal">
-#pragma db namespace() pointer(std::tr1::shared_ptr)
- </pre>
-
- <p>Note that we can always override the default pointer specified
- at the namespace level or with the command line option using
- the <code>db&nbsp;pointer</code> object or view pragma. For
- example:</p>
-
- <pre class="cxx">
-#pragma db object pointer(std::shared_ptr)
-namespace accounting
-{
- #pragma db object
- class employee
- {
- ...
- };
-
- #pragma db object pointer(std::unique_ptr)
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>Refer to <a href="#14.1.2">Section 14.1.2, "<code>pointer</code>
- (object)"</a>, <a href="#14.2.4">Section 14.2.4, "<code>pointer</code>
- (view)"</a>, and <a href="#14.5.1">Section 14.5.1, "<code>pointer</code>
- (namespace)"</a> for more information on these mechanisms.</p>
-
- <p>Built-in support that is provided by the ODB runtime library allows us
- to use <code>shared_ptr</code> (TR1 or C++11),
- <code>std::unique_ptr</code> (C++11), or <code>std::auto_ptr</code> as
- pointer types. Plus, ODB profile libraries, that are available for
- commonly used frameworks and libraries (such as Boost and Qt),
- provide support for smart pointers found in these frameworks and
- libraries (<a href="#III">Part III, "Profiles"</a>). It is also
- easy to add support for our own smart pointers, as described in
- <a href="#6.5"> Section 6.5, "Using Custom Smart Pointers"</a>.</p>
-
- <h2><a name="3.4">3.4 Database</a></h2>
-
- <p>Before an application can make use of persistence services
- offered by ODB, it has to create a database class instance. A
- database instance is the representation of the place where
- the application stores its persistent objects. We create
- a database instance by instantiating one of the database
- system-specific classes. For example, <code>odb::mysql::database</code>
- would be such a class for the MySQL database system. We will
- also normally pass a database name as an argument to the
- class' constructor. The following code fragment
- shows how we can create a database instance for the MySQL
- database system:</p>
-
- <pre class="cxx">
-#include &lt;odb/database.hxx>
-#include &lt;odb/mysql/database.hxx>
-
-auto_ptr&lt;odb::database> db (
- new odb::mysql::database (
- "test_user" // database login name
- "test_password" // database password
- "test_database" // database name
- ));
- </pre>
-
- <p>The <code>odb::database</code> class is a common interface for
- all the database system-specific classes provided by ODB. You
- would normally work with the database
- instance via this interface unless there is a specific
- functionality that your application depends on and which is
- only exposed by a particular system's <code>database</code>
- class. You will need to include the <code>&lt;odb/database.hxx></code>
- header file to make this class available in your application.</p>
-
- <p>The <code>odb::database</code> interface defines functions for
- starting transactions and manipulating persistent objects.
- These are discussed in detail in the remainder of this chapter
- as well as the next chapter which is dedicated to the topic of
- querying the database for persistent objects. For details on the
- system-specific <code>database</code> classes, refer to
- <a href="#II">Part II, "Database Systems"</a>.</p>
-
- <p>Before we can persist our objects, the corresponding database schema has
- to be created in the database. The schema contains table definitions and
- other relational database artifacts that are used to store the state of
- persistent objects in the database.</p>
-
- <p>There are several ways to create the database schema. The easiest is to
- instruct the ODB compiler to generate the corresponding schema from the
- persistent classes (<code>--generate-schema</code> option). The ODB
- compiler can generate the schema as a standalone SQL file,
- embedded into the generated C++ code, or as a separate C++ source file
- (<code>--schema-format</code> option). If we are using the SQL file
- to create the database schema, then this file should be executed,
- normally only once, before the application is started.</p>
-
- <p>Alternatively, if the schema is embedded directly into the generated
- code or produced as a separate C++ source file, then we can use the
- <code>odb::schema_catalog</code> class to create it in the database
- from within our application, for example:</p>
-
- <pre class="cxx">
-#include &lt;odb/schema-catalog.hxx>
-
-odb::transaction t (db->begin ());
-odb::schema_catalog::create_schema (*db);
-t.commit ();
- </pre>
-
- <p>Refer to the next section for information on the
- <code>odb::transaction</code> class. The complete version of the above
- code fragment is available in the <code>schema/embedded</code> example in
- the <code>odb-examples</code> package.</p>
-
- <p>The <code>odb::schema_catalog</code> class has the following interface.
- You will need to include the <code>&lt;odb/schema-catalog.hxx></code>
- header file to make this class available in your application.</p>
-
- <pre class="cxx">
-namespace odb
-{
- class schema_catalog
- {
- public:
- static void
- create_schema (database&amp;,
- const std::string&amp; name = "",
- bool drop = true);
-
- static void
- drop_schema (database&amp;, const std::string&amp; name = "");
-
- static bool
- exists (database_id, const std::string&amp; name = "");
-
- static bool
- exists (const database&amp;, const std::string&amp; name = "")
- };
-}
- </pre>
-
- <p>The first argument to the <code>create_schema()</code> function
- is the database instance that we would like to create the schema in.
- The second argument is the schema name. By default, the ODB
- compiler generates all embedded schemas with the default schema
- name (empty string). However, if your application needs to
- have several separate schemas, you can use the
- <code>--schema-name</code> ODB compiler option to assign
- custom schema names and then use these names as a second argument
- to <code>create_schema()</code>. By default, <code>create_schema()</code>
- will also delete all the database objects (tables, indexes, etc.) if
- they exist prior to creating the new ones. You can change this
- behavior by passing <code>false</code> as the third argument. The
- <code>drop_schema()</code> function allows you to delete all the
- database objects without creating the new ones.</p>
-
- <p>If the schema is not found, the <code>create_schema()</code> and
- <code>drop_schema()</code> functions throw the
- <code>odb::unknown_schema</code> exception. You can use the
- <code>exists()</code> function to check whether a schema for the
- specified database and with the specified name exists in the
- catalog. Note also that the <code>create_schema()</code> and
- <code>drop_schema()</code> functions should be called within a
- transaction.</p>
-
- <p>ODB also provides support for database schema evolution. Similar
- to schema creation, schema migration statements can be generated
- either as standalone SQL files or embedded into the generated C++
- code. For more information on schema evolution support, refer to
- <a href="#13">Chapter 13, "Database Schema Evolution"</a>.</p>
-
- <p>Finally, we can also use a custom database schema with ODB. This approach
- can work similarly to the standalone SQL file described above except that
- the database schema is hand-written or produced by another program. Or we
- could execute custom SQL statements that create the schema directly from
- our application. To map persistent classes to custom database schemas, ODB
- provides a wide range of mapping customization pragmas, such
- as <code>db&nbsp;table</code>, <code>db&nbsp;column</code>,
- and <code>db&nbsp;type</code> (<a href="#14">Chapter 14, "ODB Pragma
- Language"</a>). For sample code that shows how to perform such mapping
- for various C++ constructs, refer to the <code>schema/custom</code>
- example in the <code>odb-examples</code> package.</p>
-
- <h2><a name="3.5">3.5 Transactions</a></h2>
-
- <p>A transaction is an atomic, consistent, isolated and durable
- (ACID) unit of work. Database operations can only be
- performed within a transaction and each thread of execution
- in an application can have only one active transaction at a
- time.</p>
-
- <p>By atomicity we mean that when it comes to making changes to
- the database state within a transaction,
- either all the changes are applied or none at all. Consider,
- for example, a transaction that transfers funds between two
- objects representing bank accounts. If the debit function
- on the first object succeeds but the credit function on
- the second fails, the transaction is rolled back and the
- database state of the first object remains unchanged.</p>
-
- <p>By consistency we mean that a transaction must take all the
- objects stored in the database from one consistent state
- to another. For example, if a bank account object must
- reference a person object as its owner and we forget to
- set this reference before making the object persistent,
- the transaction will be rolled back and the database
- will remain unchanged.</p>
-
- <p>By isolation we mean that the changes made to the database
- state during a transaction are only visible inside this
- transaction until and unless it is committed. Using the
- above example with the bank transfer, the results of the
- debit operation performed on the first object is not
- visible to other transactions until the credit operation
- is successfully completed and the transaction is committed.</p>
-
- <p>By durability we mean that once the transaction is committed,
- the changes that it made to the database state are permanent
- and will survive failures such as an application crash. From
- now on the only way to alter this state is to execute and commit
- another transaction.</p>
-
- <p>A transaction is started by calling either the
- <code>database::begin()</code> or <code>connection::begin()</code>
- function. The returned transaction handle is stored in
- an instance of the <code>odb::transaction</code> class.
- You will need to include the <code>&lt;odb/transaction.hxx></code>
- header file to make this class available in your application.
- For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/transaction.hxx>
-
-transaction t (db.begin ())
-
-// Perform database operations.
-
-t.commit ();
- </pre>
-
- <p>The <code>odb::transaction</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- class transaction
- {
- public:
- typedef odb::database database_type;
- typedef odb::connection connection_type;
-
- explicit
- transaction (transaction_impl*, bool make_current = true);
-
- transaction ();
-
- void
- reset (transaction_impl*, bool make_current = true);
-
- void
- commit ();
-
- void
- rollback ();
-
- database_type&amp;
- database ();
-
- connection_type&amp;
- connection ();
-
- bool
- finilized () const;
-
- public:
- static bool
- has_current ();
-
- static transaction&amp;
- current ();
-
- static void
- current (transaction&amp;);
-
- static bool
- reset_current ();
-
- // Callback API.
- //
- public:
- ...
- };
-}
- </pre>
-
- <p>The <code>commit()</code> function commits a transaction and
- <code>rollback()</code> rolls it back. Unless the transaction
- has been <em>finalized</em>, that is, explicitly committed or rolled
- back, the destructor of the <code>transaction</code> class will
- automatically roll it back when the transaction instance goes
- out of scope. If we try to commit or roll back a finalized
- transaction, the <code>odb::transaction_already_finalized</code>
- exception is thrown.</p>
-
- <p>The <code>database()</code> accessor returns the database this
- transaction is working on. Similarly, the <code>connection()</code>
- accessor returns the database connection this transaction is on
- (<a href="#3.6">Section 3.6, "Connections"</a>).</p>
-
- <p>The static <code>current()</code> accessor returns the
- currently active transaction for this thread. If there is no active
- transaction, this function throws the <code>odb::not_in_transaction</code>
- exception. We can check whether there is a transaction in effect in
- this thread using the <code>has_current()</code> static function.</p>
-
- <p>The <code>make_current</code> argument in the <code>transaction</code>
- constructor as well as the static <code>current()</code> modifier and
- <code>reset_current()</code> function give us additional
- control over the nomination of the currently active transaction.
- If we pass <code>false</code> as the <code>make_current</code>
- argument, then the newly created transaction will not
- automatically be made the active transaction for this
- thread. Later, we can use the static <code>current()</code> modifier
- to set this transaction as the active transaction.
- The <code>reset_current()</code> static function clears the
- currently active transaction. Together, these mechanisms
- allow for more advanced use cases, such as multiplexing
- two or more transactions on the same thread. For example:</p>
-
- <pre class="cxx">
-transaction t1 (db1.begin ()); // Active transaction.
-transaction t2 (db2.begin (), false); // Not active.
-
-// Perform database operations on db1.
-
-transaction::current (t2); // Deactivate t1, activate t2.
-
-// Perform database operations on db2.
-
-transaction::current (t1); // Switch back to t1.
-
-// Perform some more database operations on db1.
-
-t1.commit ();
-
-transaction::current (t2); // Switch to t2.
-
-// Perform some more database operations on db2.
-
-t2.commit ();
- </pre>
-
- <p>The <code>reset()</code> modifier allows us to reuse the same
- <code>transaction</code> instance to complete several database
- transactions. Similar to the destructor, <code>reset()</code>
- will roll the current transaction back if it hasn't been finalized.
- The default <code>transaction</code> constructor creates a finalized
- transaction which can later be initialized using <code>reset()</code>.
- The <code>finilized()</code> accessor can be used to check whether the
- transaction has been finalized. Here is how we can use this functionality
- to commit the current transaction and start a new one every time a
- certain number of database operations has been performed:</p>
-
- <pre class="cxx">
-transaction t (db.begin ());
-
-for (size_t i (0); i &lt; n; ++i)
-{
- // Perform a database operation, such as persist an object.
-
- // Commit the current transaction and start a new one after
- // every 100 operations.
- //
- if (i % 100 == 0)
- {
- t.commit ();
- t.reset (db.begin ());
- }
-}
-
-t.commit ();
- </pre>
-
- <p>For more information on the transaction callback support, refer
- to <a href="#15.1">Section 15.1, "Transaction Callbacks"</a>.</p>
-
- <p>Note that in the above discussion of atomicity, consistency,
- isolation, and durability, all of those guarantees only apply
- to the object's state in the database as opposed to the object's
- state in the application's memory. It is possible to roll
- a transaction back but still have changes from this
- transaction in the application's memory. An easy way to
- avoid this potential inconsistency is to instantiate
- persistent objects only within the transaction scope. Consider,
- for example, these two implementations of the same transaction:</p>
-
- <pre class="cxx">
-void
-update_age (database&amp; db, person&amp; p)
-{
- transaction t (db.begin ());
-
- p.age (p.age () + 1);
- db.update (p);
-
- t.commit ();
-}
- </pre>
-
- <p>In the above implementation, if the <code>update()</code> call fails
- and the transaction is rolled back, the state of the <code>person</code>
- object in the database and the state of the same object in the
- application's memory will differ. Now consider an
- alternative implementation which only instantiates the
- <code>person</code> object for the duration of the transaction:</p>
-
- <pre class="cxx">
-void
-update_age (database&amp; db, unsigned long id)
-{
- transaction t (db.begin ());
-
- auto_ptr&lt;person> p (db.load&lt;person> (id));
- p.age (p.age () + 1);
- db.update (p);
-
- t.commit ();
-}
- </pre>
-
- <p>Of course, it may not always be possible to write the
- application in this style. Oftentimes we need to access and
- modify the application's state of persistent objects out of
- transactions. In this case it may make sense to try to
- roll back the changes made to the application state if
- the transaction was rolled back and the database state
- remains unchanged. One way to do this is to re-load
- the object's state from the database, for example:</p>
-
- <pre class="cxx">
-void
-update_age (database&amp; db, person&amp; p)
-{
- try
- {
- transaction t (db.begin ());
-
- p.age (p.age () + 1);
- db.update (p);
-
- t.commit ();
- }
- catch (...)
- {
- transaction t (db.begin ());
- db.load (p.id (), p);
- t.commit ();
-
- throw;
- }
-}
- </pre>
-
- <p>See also <a href="#15.1">Section 15.1, "Transaction Callbacks"</a>
- for an alternative approach.</p>
-
- <h2><a name="3.6">3.6 Connections</a></h2>
-
- <p>The <code>odb::connection</code> class represents a connection
- to the database. Normally, you wouldn't work with connections
- directly but rather let the ODB runtime obtain and release
- connections as needed. However, certain use cases may require
- obtaining a connection manually. For completeness, this section
- describes the <code>connection</code> class and discusses some
- of its use cases. You may want to skip this section if you are
- reading through the manual for the first time.</p>
-
- <p>Similar to <code>odb::database</code>, the <code>odb::connection</code>
- class is a common interface for all the database system-specific
- classes provided by ODB. For details on the system-specific
- <code>connection</code> classes, refer to <a href="#II">Part II,
- "Database Systems"</a>.</p>
-
- <p>To make the <code>odb::connection</code> class available in your
- application you will need to include the <code>&lt;odb/connection.hxx></code>
- header file. The <code>odb::connection</code> class has the
- following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- class connection
- {
- public:
- typedef odb::database database_type;
-
- transaction
- begin () = 0;
-
- unsigned long long
- execute (const char* statement);
-
- unsigned long long
- execute (const std::string&amp; statement);
-
- unsigned long long
- execute (const char* statement, std::size_t length);
-
- database_type&amp;
- database ();
- };
-
- typedef details::shared_ptr&lt;connection> connection_ptr;
-}
- </pre>
-
- <p>The <code>begin()</code> function is used to start a transaction
- on the connection. The <code>execute()</code> functions allow
- us to execute native database statements on the connection.
- Their semantics are equivalent to the <code>database::execute()</code>
- functions (<a href="#3.12">Section 3.12, "Executing Native SQL
- Statements"</a>) except that they can be legally called outside
- a transaction. Finally, the <code>database()</code> accessor
- returns a reference to the <code>odb::database</code> instance
- to which this connection corresponds.</p>
-
- <p>To obtain a connection we call the <code>database::connection()</code>
- function. The connection is returned as <code>odb::connection_ptr</code>,
- which is an implementation-specific smart pointer with the shared
- pointer semantics. This, in particular, means that the connection
- pointer can be copied and returned from functions. Once the last
- instance of <code>connection_ptr</code> pointing to the same
- connection is destroyed, the connection is returned to the
- <code>database</code> instance. The following code fragment
- shows how we can obtain, use, and release a connection:</p>
-
- <pre class="cxx">
-using namespace odb::core;
-
-database&amp; db = ...
-connection_ptr c (db.connection ());
-
-// Temporarily disable foreign key constraints.
-//
-c->execute ("SET FOREIGN_KEY_CHECKS = 0");
-
-// Start a transaction on this connection.
-//
-transaction t (c->begin ());
-...
-t.commit ();
-
-// Restore foreign key constraints.
-//
-c->execute ("SET FOREIGN_KEY_CHECKS = 1");
-
-// When 'c' goes out of scope, the connection is returned to 'db'.
- </pre>
-
- <p>Some of the use cases which may require direct manipulation of
- connections include out-of-transaction statement execution,
- such as the execution of connection configuration statements,
- the implementation of a connection-per-thread policy, and making
- sure that a set of transactions is executed on the same
- connection.</p>
-
- <h2><a name="3.7">3.7 Error Handling and Recovery</a></h2>
-
- <p>ODB uses C++ exceptions to report database operation errors. Most
- ODB exceptions signify <em>hard</em> errors or errors that cannot
- be corrected without some intervention from the application.
- For example, if we try to load an object with an unknown object
- id, the <code>odb::object_not_persistent</code> exception is
- thrown. Our application may be able to correct this error, for
- instance, by obtaining a valid object id and trying again.
- The hard errors and corresponding ODB exceptions that can be
- thrown by each database function are described in the remainder
- of this chapter with <a href="#3.14">Section 3.14, "ODB Exceptions"</a>
- providing a quick reference for all the ODB exceptions.</p>
-
- <p>The second group of ODB exceptions signify <em>soft</em> or
- <em>recoverable</em> errors. Such errors are temporary
- failures which normally can be corrected by simply re-executing
- the transaction. ODB defines three such exceptions:
- <code>odb::connection_lost</code>, <code>odb::timeout</code>,
- and <code>odb::deadlock</code>. All recoverable ODB exceptions
- are derived from the common <code>odb::recoverable</code> base
- exception which can be used to handle all the recoverable
- conditions with a single <code>catch</code> block.</p>
-
- <p>The <code>odb::connection_lost</code> exception is thrown if
- a connection to the database is lost in the middle of
- a transaction. In this situation the transaction is aborted but
- it can be re-tried without any changes. Similarly, the
- <code>odb::timeout</code> exception is thrown if one of the
- database operations or the whole transaction has timed out.
- Again, in this case the transaction is aborted but can be
- re-tried as is.</p>
-
- <p>If two or more transactions access or modify more than one object
- and are executed concurrently by different applications or by
- different threads within the same application, then it is possible
- that these transactions will try to access objects in an incompatible
- order and deadlock. The canonical example of a deadlock are
- two transactions in which the first has modified <code>object1</code>
- and is waiting for the second transaction to commit its changes to
- <code>object2</code> so that it can also update <code>object2</code>.
- At the same time the second transaction has modified <code>object2</code>
- and is waiting for the first transaction to commit its changes to
- <code>object1</code> because it also needs to modify <code>object1</code>.
- As a result, none of the two transactions can be completed.</p>
-
- <p>The database system detects such situations and automatically
- aborts the waiting operation in one of the deadlocked transactions.
- In ODB this translates to the <code>odb::deadlock</code>
- recoverable exception being thrown from one of the database functions.</p>
-
- <p>The following code fragment shows how to handle the recoverable
- exceptions by restarting the affected transaction:</p>
-
- <pre class="cxx">
-const unsigned short max_retries = 5;
-
-for (unsigned short retry_count (0); ; retry_count++)
-{
- try
- {
- transaction t (db.begin ());
-
- ...
-
- t.commit ();
- break;
- }
- catch (const odb::recoverable&amp; e)
- {
- if (retry_count > max_retries)
- throw retry_limit_exceeded (e.what ());
- else
- continue;
- }
-}
- </pre>
-
- <h2><a name="3.8">3.8 Making Objects Persistent</a></h2>
-
- <p>A newly created instance of a persistent class is transient.
- We use the <code>database::persist()</code> function template
- to make a transient instance persistent. This function has four
- overloaded versions with the following signatures:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- typename object_traits&lt;T>::id_type
- persist (const T&amp; object);
-
- template &lt;typename T>
- typename object_traits&lt;T>::id_type
- persist (const object_traits&lt;T>::const_pointer_type&amp; object);
-
- template &lt;typename T>
- typename object_traits&lt;T>::id_type
- persist (T&amp; object);
-
- template &lt;typename T>
- typename object_traits&lt;T>::id_type
- persist (const object_traits&lt;T>::pointer_type&amp; object);
- </pre>
-
- <p>Here and in the rest of the manual,
- <code>object_traits&lt;T>::pointer_type</code> and
- <code>object_traits&lt;T>::const_pointer_type</code> denote the
- unrestricted and constant object pointer types (<a href="#3.3">Section
- 3.3, "Object and View Pointers"</a>), respectively.
- Similarly, <code>object_traits&lt;T>::id_type</code> denotes the object
- id type. The <code>odb::object_traits</code> template is part of the
- database support code generated by the ODB compiler.</p>
-
- <p>The first <code>persist()</code> function expects a constant reference
- to an instance being persisted. The second function expects a constant
- object pointer. Both of these functions can only be used on objects with
- application-assigned object ids (<a href="#14.4.2">Section 14.4.2,
- "<code>auto</code>"</a>).</p>
-
- <p>The second and third <code>persist()</code> functions are similar to the
- first two except that they operate on unrestricted references and object
- pointers. If the identifier of the object being persisted is assigned
- by the database, these functions update the id member of the passed
- instance with the assigned value. All four functions return the object
- id of the newly persisted object.</p>
-
- <p>If the database already contains an object of this type with this
- identifier, the <code>persist()</code> functions throw the
- <code>odb::object_already_persistent</code> exception. This should
- never happen for database-assigned object ids as long as the
- number of objects persisted does not exceed the value space of
- the id type.</p>
-
- <p>When calling the <code>persist()</code> functions, we don't need to
- explicitly specify the template type since it will be automatically
- deduced from the argument being passed. The following example shows
- how we can call these functions:</p>
-
- <pre class="cxx">
-person john ("John", "Doe", 33);
-shared_ptr&lt;person> jane (new person ("Jane", "Doe", 32));
-
-transaction t (db.begin ());
-
-db.persist (john);
-unsigned long jane_id (db.persist (jane));
-
-t.commit ();
-
-cerr &lt;&lt; "Jane's id: " &lt;&lt; jane_id &lt;&lt; endl;
- </pre>
-
- <p>Notice that in the above code fragment we have created instances
- that we were planning to make persistent before starting the
- transaction. Likewise, we printed Jane's id after we have committed
- the transaction. As a general rule, you should avoid performing
- operations within the transaction scope that can be performed
- before the transaction starts or after it terminates. An active
- transaction consumes both your application's resources, such as
- a database connection, as well as the database server's
- resources, such as object locks. By following the above rule you
- make sure these resources are released and made available to other
- threads in your application and to other applications as soon as
- possible.</p>
-
- <p>Some database systems support persisting multiple objects with a
- single underlying statement execution which can result in significantly
- improved performance. For such database systems ODB provides
- bulk <code>persist()</code> functions. For details, refer to
- <a href="#15.3">Section 15.3, "Bulk Database Operations"</a>.</p>
-
- <h2><a name="3.9">3.9 Loading Persistent Objects</a></h2>
-
- <p>Once an object is made persistent, and you know its object id, it
- can be loaded by the application using the <code>database::load()</code>
- function template. This function has two overloaded versions with
- the following signatures:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- typename object_traits&lt;T>::pointer_type
- load (const typename object_traits&lt;T>::id_type&amp; id);
-
- template &lt;typename T>
- void
- load (const typename object_traits&lt;T>::id_type&amp; id, T&amp; object);
- </pre>
-
- <p>Given an object id, the first function allocates a new instance
- of the object class in the dynamic memory, loads its state from
- the database, and returns the pointer to the new instance. The
- second function loads the object's state into an existing instance.
- Both functions throw <code>odb::object_not_persistent</code> if
- there is no object of this type with this id in the database.</p>
-
- <p>When we call the first <code>load()</code> function, we need to
- explicitly specify the object type. We don't need to do this for
- the second function because the object type will be automatically
- deduced from the second argument, for example:</p>
-
- <pre class="cxx">
-transaction t (db.begin ());
-
-auto_ptr&lt;person> jane (db.load&lt;person> (jane_id));
-
-db.load (jane_id, *jane);
-
-t.commit ();
- </pre>
-
- <p>In certain situations it may be necessary to reload the state
- of an object from the database. While this is easy to achieve
- using the second <code>load()</code> function, ODB provides
- the <code>database::reload()</code> function template that
- has a number of special properties. This function has two
- overloaded versions with the following signatures:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- void
- reload (T&amp; object);
-
- template &lt;typename T>
- void
- reload (const object_traits&lt;T>::pointer_type&amp; object);
- </pre>
-
- <p>The first <code>reload()</code> function expects an object
- reference, while the second expects an object pointer. Both
- functions expect the id member in the passed object to contain
- a valid object identifier and, similar to <code>load()</code>,
- both will throw <code>odb::object_not_persistent</code> if
- there is no object of this type with this id in the database.</p>
-
- <p>The first special property of <code>reload()</code>
- compared to the <code>load()</code> function is that it
- does not interact with the session's object cache
- (<a href="#11.1">Section 11.1, "Object Cache"</a>). That is, if
- the object being reloaded is already in the cache, then it will
- remain there after <code>reload()</code> returns. Similarly, if the
- object is not in the cache, then <code>reload()</code> won't
- put it there either.</p>
-
- <p>The second special property of the <code>reload()</code> function
- only manifests itself when operating on an object with the optimistic
- concurrency model. In this case, if the states of the object
- in the application memory and in the database are the same, then
- no reloading will occur. For more information on optimistic
- concurrency, refer to <a href="#12">Chapter 12, "Optimistic
- Concurrency"</a>.</p>
-
- <p>If we don't know for sure whether an object with a given id
- is persistent, we can use the <code>find()</code> function
- instead of <code>load()</code>, for example:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- typename object_traits&lt;T>::pointer_type
- find (const typename object_traits&lt;T>::id_type&amp; id);
-
- template &lt;typename T>
- bool
- find (const typename object_traits&lt;T>::id_type&amp; id, T&amp; object);
- </pre>
-
- <p>If an object with this id is not found in the database, the first
- <code>find()</code> function returns a <code>NULL</code> pointer
- while the second function leaves the passed instance unmodified and
- returns <code>false</code>.</p>
-
- <p>If we don't know the object id, then we can use queries to
- find the object (or objects) matching some criteria
- (<a href="#4">Chapter 4, "Querying the Database"</a>). Note,
- however, that loading an object's state using its
- identifier can be significantly faster than executing a query.</p>
-
-
- <h2><a name="3.10">3.10 Updating Persistent Objects</a></h2>
-
- <p>If a persistent object has been modified, we can store the updated
- state in the database using the <code>database::update()</code>
- function template. This function has three overloaded versions with
- the following signatures:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- void
- update (const T&amp; object);
-
- template &lt;typename T>
- void
- update (const object_traits&lt;T>::const_pointer_type&amp; object);
-
- template &lt;typename T>
- void
- update (const object_traits&lt;T>::pointer_type&amp; object);
- </pre>
-
- <p>The first <code>update()</code> function expects an object reference,
- while the other two expect object pointers. If the object passed to
- one of these functions does not exist in the database,
- <code>update()</code> throws the <code>odb::object_not_persistent</code>
- exception (but see a note on optimistic concurrency below).</p>
-
- <p>Below is an example of the funds transfer that we talked about
- in the earlier section on transactions. It uses the hypothetical
- <code>bank_account</code> persistent class:</p>
-
- <pre class="cxx">
-void
-transfer (database&amp; db,
- unsigned long from_acc,
- unsigned long to_acc,
- unsigned int amount)
-{
- bank_account from, to;
-
- transaction t (db.begin ());
-
- db.load (from_acc, from);
-
- if (from.balance () &lt; amount)
- throw insufficient_funds ();
-
- db.load (to_acc, to);
-
- to.balance (to.balance () + amount);
- from.balance (from.balance () - amount);
-
- db.update (to);
- db.update (from);
-
- t.commit ();
-}
- </pre>
-
- <p>The same can be accomplished using dynamically allocated objects
- and the <code>update()</code> function with object pointer argument,
- for example:</p>
-
- <pre class="cxx">
-transaction t (db.begin ());
-
-shared_ptr&lt;bank_account> from (db.load&lt;bank_account> (from_acc));
-
-if (from->balance () &lt; amount)
- throw insufficient_funds ();
-
-shared_ptr&lt;bank_account> to (db.load&lt;bank_account> (to_acc));
-
-to->balance (to->balance () + amount);
-from->balance (from->balance () - amount);
-
-db.update (to);
-db.update (from);
-
-t.commit ();
- </pre>
-
- <p>If any of the <code>update()</code> functions are operating on a
- persistent class with the optimistic concurrency model, then they will
- throw the <code>odb::object_changed</code> exception if the state of the
- object in the database has changed since it was last loaded into the
- application memory. Furthermore, for such classes, <code>update()</code>
- no longer throws the <code>object_not_persistent</code> exception if
- there is no such object in the database. Instead, this condition is
- treated as a change of object state and <code>object_changed</code>
- is thrown instead. For a more detailed discussion of optimistic
- concurrency, refer to <a href="#12">Chapter 12, "Optimistic
- Concurrency"</a>.</p>
-
- <p>In ODB, persistent classes, composite value types, as well as individual
- data members can be declared read-only (see <a href="#14.1.4">Section
- 14.1.4, "<code>readonly</code> (object)"</a>, <a href="#14.3.6">Section
- 14.3.6, "<code>readonly</code> (composite value)"</a>, and
- <a href="#14.4.12">Section 14.4.12, "<code>readonly</code>
- (data member)"</a>).</p>
-
- <p>If an individual data member is declared read-only, then
- any changes to this member will be ignored when updating the database
- state of an object using any of the above <code>update()</code>
- functions. A <code>const</code> data member is automatically treated
- as read-only. If a composite value is declared read-only then all its
- data members are treated as read-only.</p>
-
- <p>If the whole object is declared read-only then the database state of
- this object cannot be changed. Calling any of the above
- <code>update()</code> functions for such an object will result in a
- compile-time error.</p>
-
- <p>Similar to <code>persist()</code>, for database systems that support
- this functionality, ODB provides bulk <code>update()</code> functions.
- For details, refer to <a href="#15.3">Section 15.3, "Bulk Database
- Operations"</a>.</p>
-
- <h2><a name="3.11">3.11 Deleting Persistent Objects</a></h2>
-
- <p>To delete a persistent object's state from the database we use the
- <code>database::erase()</code> or <code>database::erase_query()</code>
- function templates. If the application still has an instance of the
- erased object, this instance becomes transient. The <code>erase()</code>
- function has the following overloaded versions:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- void
- erase (const T&amp; object);
-
- template &lt;typename T>
- void
- erase (const object_traits&lt;T>::const_pointer_type&amp; object);
-
- template &lt;typename T>
- void
- erase (const object_traits&lt;T>::pointer_type&amp; object);
-
- template &lt;typename T>
- void
- erase (const typename object_traits&lt;T>::id_type&amp; id);
- </pre>
-
- <p>The first <code>erase()</code> function uses an object itself, in
- the form of an object reference, to delete its state from the
- database. The next two functions accomplish the same result but using
- object pointers. Note that all three functions leave the passed
- object unchanged. It simply becomes transient. The last function
- uses the object id to identify the object to be deleted. If the
- object does not exist in the database, then all four functions
- throw the <code>odb::object_not_persistent</code> exception
- (but see a note on optimistic concurrency below).</p>
-
- <p>We have to specify the object type when calling the last
- <code>erase()</code> function. The same is unnecessary for the
- first three functions because the object type will be automatically
- deduced from their arguments. The following example shows how we
- can call these functions:</p>
-
- <pre class="cxx">
-person&amp; john = ...
-shared_ptr&lt;jane> jane = ...
-unsigned long joe_id = ...
-
-transaction t (db.begin ());
-
-db.erase (john);
-db.erase (jane);
-db.erase&lt;person> (joe_id);
-
-t.commit ();
- </pre>
-
- <p>If any of the <code>erase()</code> functions except the last one are
- operating on a persistent class with the optimistic concurrency
- model, then they will throw the <code>odb::object_changed</code> exception
- if the state of the object in the database has changed since it was
- last loaded into the application memory. Furthermore, for such
- classes, <code>erase()</code> no longer throws the
- <code>object_not_persistent</code> exception if there is no such
- object in the database. Instead, this condition is treated as a
- change of object state and <code>object_changed</code> is thrown
- instead. For a more detailed discussion of optimistic concurrency,
- refer to <a href="#12">Chapter 12, "Optimistic Concurrency"</a>.</p>
-
- <p>Similar to <code>persist()</code> and <code>update()</code>, for
- database systems that support this functionality, ODB provides
- bulk <code>erase()</code> functions. For details, refer to
- <a href="#15.3">Section 15.3, "Bulk Database Operations"</a>.</p>
-
- <p>The <code>erase_query()</code> function allows us to delete
- the state of multiple objects matching certain criteria. It uses
- the query expression of the <code>database::query()</code> function
- (<a href="#4">Chapter 4, "Querying the Database"</a>) and,
- because the ODB query facility is optional, it is only available
- if the <code>--generate-query</code> ODB compiler option was
- specified. The <code>erase_query()</code> function has the
- following overloaded versions:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- unsigned long long
- erase_query ();
-
- template &lt;typename T>
- unsigned long long
- erase_query (const odb::query&lt;T>&amp;);
- </pre>
-
- <p>The first <code>erase_query()</code> function is used to delete
- the state of all the persistent objects of a given type stored
- in the database. The second function uses the passed query instance
- to only delete the state of objects matching the query criteria.
- Both functions return the number of objects erased. When calling
- the <code>erase_query()</code> function, we have to explicitly
- specify the object type we are erasing. For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;person> query;
-
-transaction t (db.begin ());
-
-db.erase_query&lt;person> (query::last == "Doe" &amp;&amp; query::age &lt; 30);
-
-t.commit ();
- </pre>
-
- <p>Unlike the <code>query()</code> function, when calling
- <code>erase_query()</code> we cannot use members from pointed-to
- objects in the query expression. However, we can still use
- a member corresponding to a pointer as an ordinary object
- member that has the id type of the pointed-to object
- (<a href="#6">Chapter 6, "Relationships"</a>). This allows us
- to compare object ids as well as test the pointer for
- <code>NULL</code>. As an example, the following transaction
- makes sure that all the <code>employee</code> objects that
- reference an <code>employer</code> object that is about to
- be deleted are deleted as well. Here we assume that the
- <code>employee</code> class contains a pointer to the
- <code>employer</code> class. Refer to <a href="#6">Chapter 6,
- "Relationships"</a> for complete definitions of these
- classes.</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee> query;
-
-transaction t (db.begin ());
-
-employer&amp; e = ... // Employer object to be deleted.
-
-db.erase_query&lt;employee> (query::employer == e.id ());
-db.erase (e);
-
-t.commit ();
- </pre>
-
-
- <h2><a name="3.12">3.12 Executing Native SQL Statements</a></h2>
-
- <p>In some situations we may need to execute native SQL statements
- instead of using the object-oriented database API described above.
- For example, we may want to tune the database schema generated
- by the ODB compiler or take advantage of a feature that is
- specific to the database system we are using. The
- <code>database::execute()</code> function, which has three
- overloaded versions, provides this functionality:</p>
-
- <pre class="cxx">
- unsigned long long
- execute (const char* statement);
-
- unsigned long long
- execute (const std::string&amp; statement);
-
- unsigned long long
- execute (const char* statement, std::size_t length)
- </pre>
-
- <p>The first <code>execute()</code> function expects the SQL statement
- as a zero-terminated C-string. The last version expects the explicit
- statement length as the second argument and the statement itself
- may contain <code>'\0'</code> characters, for example, to represent
- binary data, if the database system supports it. All three functions
- return the number of rows that were affected by the statement. For
- example:</p>
-
- <pre class="cxx">
-transaction t (db.begin ());
-
-db.execute ("DROP TABLE test");
-db.execute ("CREATE TABLE test (n INT PRIMARY KEY)");
-
-t.commit ();
- </pre>
-
- <p>While these functions must always be called within a transaction,
- it may be necessary to execute a native statement outside a
- transaction. This can be done using the
- <code>connection::execute()</code> functions as described in
- <a href="#3.6">Section 3.6, "Connections"</a>.</p>
-
- <h2><a name="3.13">3.13 Tracing SQL Statement Execution</a></h2>
-
- <p>Oftentimes it is useful to understand what SQL statements are
- executed as a result of high-level database operations. For
- example, we can use this information to figure out why certain
- transactions don't produce desired results or why they take
- longer than expected.</p>
-
- <p>While this information can usually be obtained from the database
- logs, ODB provides an application-side SQL statement tracing
- support that is both more convenient and finer-grained.
- For example, in a typical situation that calls for tracing
- we would like to see the SQL statements executed as a result
- of a specific transaction. While it may be difficult to
- extract such a subset of statements from the database logs,
- it is easy to achieve with ODB tracing support:</p>
-
- <pre class="cxx">
-transaction t (db.begin ());
-t.tracer (stderr_tracer);
-
-...
-
-t.commit ();
- </pre>
-
- <p>ODB allows us to specify a tracer on the database, connection,
- and transaction levels. If specified for the database, then
- all the statements executed on this database will be traced.
- On the other hand, if a tracer is specified for the
- connection, then only the SQL statements executed on this
- connection will be traced. Similarly, a tracer specified
- for a transaction will only show statements that are
- executed as part of this transaction. All three classes
- (<code>odb::database</code>, <code>odb::connection</code>,
- and <code>odb::transaction</code>) provide the identical
- tracing API:</p>
-
- <pre class="cxx">
- void
- tracer (odb::tracer&amp;);
-
- void
- tracer (odb::tracer*);
-
- odb::tracer*
- tracer () const;
- </pre>
-
- <p>The first two <code>tracer()</code> functions allow us to set
- the tracer object with the second one allowing us to clear the
- current tracer by passing a <code>NULL</code> pointer. The
- last <code>tracer()</code> function allows us to get the
- current tracer object. It returns a <code>NULL</code> pointer
- if there is no tracer in effect. Note that the tracing API
- does not manage the lifetime of the tracer object. The tracer
- should be valid for as long as it is being used. Furthermore,
- the tracing API is not thread-safe. Trying to set a tracer
- from multiple threads simultaneously will result in
- undefined behavior.</p>
-
- <p>The <code>odb::tracer</code> class defines a callback interface
- that can be used to create custom tracer implementations. The
- <code>odb::stderr_tracer</code> and <code>odb::stderr_full_tracer</code>
- are built-in tracer implementations provided by the ODB runtime.
- They both print SQL statements being executed to the standard error
- stream. The full tracer, in addition to tracing statement executions,
- also traces their preparations and deallocations. One situation where
- the full tracer can be particularly useful is if a statement (for
- example a custom query) contains a syntax error. In this case the
- error will be detected during preparation and, as a result, the
- statement will never be executed. The only way to see such a statement
- is by using the full tracing.</p>
-
- <p>The <code>odb::tracer</code> class is defined in the
- <code>&lt;odb/tracer.hxx></code> header file which you will need to
- include in order to make this class available in your application.
- The <code>odb::tracer</code> interface provided the following
- callback functions:</p>
-
- <pre class="cxx">
-namespace odb
-{
- class tracer
- {
- public:
- virtual void
- prepare (connection&amp;, const statement&amp;);
-
- virtual void
- execute (connection&amp;, const statement&amp;);
-
- virtual void
- execute (connection&amp;, const char* statement) = 0;
-
- virtual void
- deallocate (connection&amp;, const statement&amp;);
- };
-}
- </pre>
-
- <p>The <code>prepare()</code> and <code>deallocate()</code> functions
- are called when a prepared statement is created and destroyed,
- respectively. The first <code>execute()</code> function is called
- when a prepared statement is executed while the second one is called
- when a normal statement is executed. The default implementations
- for the <code>prepare()</code> and <code>deallocate()</code>
- functions do nothing while the first <code>execute()</code> function
- calls the second one passing the statement text as the second
- argument. As a result, if all you are interested in are the
- SQL statements being executed, then you only need to override the
- second <code>execute()</code> function.</p>
-
- <p>In addition to the common <code>odb::tracer</code> interface,
- each database runtime provides a database-specific version
- as <code>odb::&lt;database>::tracer</code>. It has exactly
- the same interface as the common version except that the
- <code>connection</code> and <code>statement</code> types
- are database-specific, which gives us access to additional,
- database-specific information.</p>
-
- <p>As an example, consider a more elaborate, PostgreSQL-specific
- tracer implementation. Here we rely on the fact that the PostgreSQL
- ODB runtime uses names to identify prepared statements and this
- information can be obtained from the <code>odb::pgsql::statement</code>
- object:</p>
-
- <pre class="cxx">
-#include &lt;odb/pgsql/tracer.hxx>
-#include &lt;odb/pgsql/database.hxx>
-#include &lt;odb/pgsql/connection.hxx>
-#include &lt;odb/pgsql/statement.hxx>
-
-class pgsql_tracer: public odb::pgsql::tracer
-{
- virtual void
- prepare (odb::pgsql::connection&amp; c, const odb::pgsql::statement&amp; s)
- {
- cerr &lt;&lt; c.database ().db () &lt;&lt; ": PREPARE " &lt;&lt; s.name ()
- &lt;&lt; " AS " &lt;&lt; s.text () &lt;&lt; endl;
- }
-
- virtual void
- execute (odb::pgsql::connection&amp; c, const odb::pgsql::statement&amp; s)
- {
- cerr &lt;&lt; c.database ().db () &lt;&lt; ": EXECUTE " &lt;&lt; s.name () &lt;&lt; endl;
- }
-
- virtual void
- execute (odb::pgsql::connection&amp; c, const char* statement)
- {
- cerr &lt;&lt; c.database ().db () &lt;&lt; ": " &lt;&lt; statement &lt;&lt; endl;
- }
-
- virtual void
- deallocate (odb::pgsql::connection&amp; c, const odb::pgsql::statement&amp; s)
- {
- cerr &lt;&lt; c.database ().db () &lt;&lt; ": DEALLOCATE " &lt;&lt; s.name () &lt;&lt; endl;
- }
-};
- </pre>
-
- <p>Note also that you can only set a database-specific tracer object
- using a database-specific database instance, for example:</p>
-
- <pre class="cxx">
-pgsql_tracer tracer;
-
-odb::database&amp; db = ...;
-db.tracer (tracer); // Compile error.
-
-odb::pgsql::database&amp; db = ...;
-db.tracer (tracer); // Ok.
- </pre>
-
- <h2><a name="3.14">3.14 ODB Exceptions</a></h2>
-
- <p>In the previous sections we have already mentioned some of the
- exceptions that can be thrown by the database functions. In this
- section we will discuss the ODB exception hierarchy and document
- all the exceptions that can be thrown by the common ODB
- runtime.</p>
-
- <p>The root of the ODB exception hierarchy is the abstract
- <code>odb::exception</code> class. This class derives
- from <code>std::exception</code> and has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- struct exception: std::exception
- {
- virtual const char*
- what () const throw () = 0;
- };
-}
- </pre>
-
- <p>Catching this exception guarantees that we will catch all the
- exceptions thrown by ODB. The <code>what()</code> function
- returns a human-readable description of the condition that
- triggered the exception.</p>
-
- <p>The concrete exceptions that can be thrown by ODB are presented
- in the following listing:</p>
-
- <pre class="cxx">
-namespace odb
-{
- struct null_pointer: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- // Transaction exceptions.
- //
- struct already_in_transaction: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct not_in_transaction: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct transaction_already_finalized: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- // Session exceptions.
- //
- struct already_in_session: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct not_in_session: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct session_required: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- // Database operations exceptions.
- //
- struct recoverable: exception
- {
- };
-
- struct connection_lost: recoverable
- {
- virtual const char*
- what () const throw ();
- };
-
- struct timeout: recoverable
- {
- virtual const char*
- what () const throw ();
- };
-
- struct deadlock: recoverable
- {
- virtual const char*
- what () const throw ();
- };
-
- struct object_not_persistent: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct object_already_persistent: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct object_changed: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct result_not_cached: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct database_exception: exception
- {
- };
-
- // Polymorphism support exceptions.
- //
- struct abstract_class: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct no_type_info: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- // Prepared query support exceptions.
- //
- struct prepared_already_cached: exception
- {
- const char*
- name () const;
-
- virtual const char*
- what () const throw ();
- };
-
- struct prepared_type_mismatch: exception
- {
- const char*
- name () const;
-
- virtual const char*
- what () const throw ();
- };
-
- // Schema catalog exceptions.
- //
- struct unknown_schema: exception
- {
- const std::string&amp;
- name () const;
-
- virtual const char*
- what () const throw ();
- };
-
- struct unknown_schema_version: exception
- {
- schema_version
- version () const;
-
- virtual const char*
- what () const throw ();
- };
-
- // Section exceptions.
- //
- struct section_not_loaded: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct section_not_in_object: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- // Bulk operation exceptions.
- //
- struct multiple_exceptions: exception
- {
- ...
-
- virtual const char*
- what () const throw ();
- };
-}
- </pre>
-
- <p>The <code>null_pointer</code> exception is thrown when a
- pointer to a persistent object declared non-<code>NULL</code>
- with the <code>db&nbsp;not_null</code> or
- <code>db&nbsp;value_not_null</code> pragma has the <code>NULL</code>
- value. See <a href="#6">Chapter 6, "Relationships"</a> for details.</p>
-
- <p>The next three exceptions (<code>already_in_transaction</code>,
- <code>not_in_transaction</code>,
- <code>transaction_already_finalized</code>) are thrown by the
- <code>odb::transaction</code> class and are discussed
- in <a href="#3.5">Section 3.5, "Transactions"</a>.</p>
-
- <p>The next two exceptions (<code>already_in_session</code>, and
- <code>not_in_session</code>) are thrown by the <code>odb::session</code>
- class and are discussed in <a href="#11">Chapter 11, "Session"</a>.</p>
-
- <p>The <code>session_required</code> exception is thrown when ODB detects
- that correctly loading a bidirectional object relationship requires a
- session but one is not used. See <a href="#6.2">Section 6.2,
- "Bidirectional Relationships"</a> for more information on this
- exception.</p>
-
- <p>The <code>recoverable</code> exception serves as a common base
- for all the recoverable exceptions, which are: <code>connection_lost</code>,
- <code>timeout</code>, and <code>deadlock</code>. The
- <code>connection_lost</code> exception is thrown when a connection
- to the database is lost. Similarly, the <code>timeout</code> exception
- is thrown if one of the database operations or the whole transaction
- has timed out. The <code>deadlock</code> exception is thrown when a
- transaction deadlock is detected by the database system. These
- exceptions can be thrown by any database function. See
- <a href="#3.7">Section 3.7, "Error Handling and Recovery"</a>
- for details.</p>
-
- <p>The <code>object_already_persistent</code> exception is thrown
- by the <code>persist()</code> database function. See
- <a href="#3.8">Section 3.8, "Making Objects Persistent"</a>
- for details.</p>
-
- <p>The <code>object_not_persistent</code> exception is thrown
- by the <code>load()</code>, <code>update()</code>, and
- <code>erase()</code> database functions. Refer to
- <a href="#3.9">Section 3.9, "Loading Persistent Objects"</a>,
- <a href="#3.10">Section 3.10, "Updating Persistent Objects"</a>, and
- <a href="#3.11">Section 3.11, "Deleting Persistent Objects"</a> for
- more information.</p>
-
- <p>The <code>object_changed</code> exception is thrown
- by the <code>update()</code> database function and certain
- <code>erase()</code> database functions when
- operating on objects with the optimistic concurrency model. See
- <a href="#12">Chapter 12, "Optimistic Concurrency"</a> for details.</p>
-
- <p>The <code>result_not_cached</code> exception is thrown by
- the query result class. Refer to <a href="#4.4">Section 4.4,
- "Query Result"</a> for details.</p>
-
- <p>The <code>database_exception</code> exception is a base class for all
- database system-specific exceptions that are thrown by the
- database system-specific runtime library. Refer to <a href="#II">Part
- II, "Database Systems"</a> for more information.</p>
-
- <p>The <code>abstract_class</code> exception is thrown by the database
- functions when we attempt to persist, update, load, or erase an
- instance of a polymorphic abstract class. For more information
- on abstract classes, refer to <a href="#14.1.3">Section 14.1.3,
- "<code>abstract</code>"</a>.</p>
-
- <p>The <code>no_type_info</code> exception is thrown by the database
- functions when we attempt to persist, update, load, or erase an
- instance of a polymorphic class for which no type information
- is present in the application. This normally means that the
- generated database support code for this class has not been
- linked (or dynamically loaded) into the application or the
- discriminator value has not been mapped to a persistent
- class. For more information on polymorphism support, refer to
- <a href="#8.2">Section 8.2, "Polymorphism Inheritance"</a>.</p>
-
- <p>The <code>prepared_already_cached</code> exception is thrown by the
- <code>cache_query()</code> function if a prepared query with the
- specified name is already cached. The <code>prepared_type_mismatch</code>
- exception is thrown by the <code>lookup_query()</code> function if
- the specified prepared query object type or parameters type
- does not match the one in the cache. Refer to <a href="#4.5">Section
- 4.5, "Prepared Queries"</a> for details.</p>
-
- <p>The <code>unknown_schema</code> exception is thrown by the
- <code>odb::schema_catalog</code> class if a schema with the specified
- name is not found. Refer to <a href="#3.4">Section 3.4, "Database"</a>
- for details. The <code>unknown_schema_version</code> exception is thrown
- by the <code>schema_catalog</code> functions that deal with database
- schema evolution if the passed or current version is unknow. Refer
- to <a href="#13">Chapter 13, "Database Schema Evolution"</a> for
- details.</p>
-
- <p>The <code>section_not_loaded</code> exception is thrown if we
- attempt to update an object section that hasn't been loaded.
- The <code>section_not_in_object</code> exception is thrown if
- the section instance being loaded or updated does not belong
- to the corresponding object. See <a href="#9">Chapter 9,
- "Sections"</a> for more information on these exceptions.</p>
-
- <p>The <code>multiple_exceptions</code> exception is thrown by the
- bulk API functions. Refer to <a href="#15.3">Section 15.3, "Bulk
- Database Operations"</a> for details.</p>
-
- <p>The <code>odb::exception</code> class is defined in the
- <code>&lt;odb/exception.hxx></code> header file. All the
- concrete ODB exceptions are defined in
- <code>&lt;odb/exceptions.hxx></code> which also includes
- <code>&lt;odb/exception.hxx></code>. Normally you don't
- need to include either of these two headers because they are
- automatically included by <code>&lt;odb/database.hxx></code>.
- However, if the source file that handles ODB exceptions
- does not include <code>&lt;odb/database.hxx></code>, then
- you will need to explicitly include one of these headers.</p>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="4">4 Querying the Database</a></h1>
-
- <p>If we don't know the identifiers of the objects that we are looking
- for, we can use queries to search the database for objects matching
- certain criteria. The ODB query facility is optional and we need to
- explicitly request the generation of the necessary database support
- code with the <code>--generate-query</code> ODB compiler option.</p>
-
- <p>ODB provides a flexible query API that offers two distinct levels of
- abstraction from the database system query language such as SQL.
- At the high level we are presented with an easy to use yet powerful
- object-oriented query language, called ODB Query Language. This
- query language is modeled after and is integrated into C++ allowing
- us to write expressive and safe queries that look and feel like
- ordinary C++. We have already seen examples of these queries in the
- introductory chapters. Below is another, more interesting, example:</p>
-
- <pre class="cxx">
- typedef odb::query&lt;person> query;
- typedef odb::result&lt;person> result;
-
- unsigned short age;
- query q (query::first == "John" &amp;&amp; query::age &lt; query::_ref (age));
-
- for (age = 10; age &lt; 100; age += 10)
- {
- result r (db.query&lt;person> (q));
- ...
- }
- </pre>
-
- <p>At the low level, queries can be written as predicates using
- the database system-native query language such as the
- <code>WHERE</code> predicate from the SQL <code>SELECT</code>
- statement. This language will be referred to as native query
- language. At this level ODB still takes care of converting
- query parameters from C++ to the database system format. Below
- is the re-implementation of the above example using SQL as
- the native query language:</p>
-
- <pre class="cxx">
- query q ("first = 'John' AND age = " + query::_ref (age));
- </pre>
-
- <p>Note that at this level we lose the static typing of
- query expressions. For example, if we wrote something like this:</p>
-
- <pre class="cxx">
- query q (query::first == 123 &amp;&amp; query::agee &lt; query::_ref (age));
- </pre>
-
- <p>We would get two errors during the C++ compilation. The first would
- indicate that we cannot compare <code>query::first</code> to an
- integer and the second would pick the misspelling in
- <code>query::agee</code>. On the other hand, if we wrote something
- like this:</p>
-
- <pre class="cxx">
- query q ("first = 123 AND agee = " + query::_ref (age));
- </pre>
-
- <p>It would compile fine and would trigger an error only when executed
- by the database system.</p>
-
- <p>We can also combine the two query languages in a single query, for
- example:</p>
-
- <pre class="cxx">
- query q ("first = 'John' AND" + (query::age &lt; query::_ref (age)));
- </pre>
-
- <h2><a name="4.1">4.1 ODB Query Language</a></h2>
-
- <p>An ODB query is an expression that tells the database system whether
- any given object matches the desired criteria. As such, a query expression
- always evaluates as <code>true</code> or <code>false</code>. At
- the higher level, an expression consists of other expressions
- combined with logical operators such as <code>&amp;&amp;</code> (AND),
- <code>||</code> (OR), and <code>!</code> (NOT). For example:</p>
-
- <pre class="cxx">
- typedef odb::query&lt;person> query;
-
- query q (query::first == "John" || query::age == 31);
- </pre>
-
- <p>At the core of every query expression lie simple expressions which
- involve one or more object members, values, or parameters. To
- refer to an object member we use an expression such as
- <code>query::first</code> above. The names of members in the
- <code>query</code> class are derived from the names of data members
- in the object class by removing the common member name decorations,
- such as leading and trailing underscores, the <code>m_</code> prefix,
- etc.</p>
-
- <p>In a simple expression an object member can be compared to a value,
- parameter, or another member using a number of predefined operators
- and functions. The following table gives an overview of the available
- expressions:</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="operators" border="1">
- <tr>
- <th>Operator</th>
- <th>Description</th>
- <th>Example</th>
- </tr>
-
- <tr>
- <td><code>==</code></td>
- <td>equal</td>
- <td><code>query::age == 31</code></td>
- </tr>
-
- <tr>
- <td><code>!=</code></td>
- <td>unequal</td>
- <td><code>query::age != 31</code></td>
- </tr>
-
- <tr>
- <td><code>&lt;</code></td>
- <td>less than</td>
- <td><code>query::age &lt; 31</code></td>
- </tr>
-
- <tr>
- <td><code>></code></td>
- <td>greater than</td>
- <td><code>query::age > 31</code></td>
- </tr>
-
- <tr>
- <td><code>&lt;=</code></td>
- <td>less than or equal</td>
- <td><code>query::age &lt;= 31</code></td>
- </tr>
-
- <tr>
- <td><code>>=</code></td>
- <td>greater than or equal</td>
- <td><code>query::age >= 31</code></td>
- </tr>
-
- <tr>
- <td><code>in()</code></td>
- <td>one of the values</td>
- <td><code>query::age.in (30, 32, 34)</code></td>
- </tr>
-
- <tr>
- <td><code>in_range()</code></td>
- <td>one of the values in range</td>
- <td><code>query::age.in_range (begin, end)</code></td>
- </tr>
-
- <tr>
- <td><code>like()</code></td>
- <td>matches a pattern</td>
- <td><code>query::first.like ("J%")</code></td>
- </tr>
-
- <tr>
- <td><code>is_null()</code></td>
- <td>value is <code>NULL</code></td>
- <td><code>query::age.is_null ()</code></td>
- </tr>
-
- <tr>
- <td><code>is_not_null()</code></td>
- <td>value is <code>NOT NULL</code></td>
- <td><code>query::age.is_not_null ()</code></td>
- </tr>
- </table>
-
- <p>The <code>in()</code> function accepts a maximum of five arguments.
- Use the <code>in_range()</code> function if you need to compare
- to more than five values. This function accepts a pair of
- standard C++ iterators and compares to all the values from
- the <code>begin</code> position inclusive and until and
- excluding the <code>end</code> position. The following
- code fragment shows how we can use these functions:</p>
-
- <pre class="cxx">
- std::vector&lt;string> names;
-
- names.push_back ("John");
- names.push_back ("Jack");
- names.push_back ("Jane");
-
- query q1 (query::first.in ("John", "Jack", "Jane"));
- query q2 (query::first.in_range (names.begin (), names.end ()));
- </pre>
-
- <p>Note that the <code>like()</code> function does not perform any
- translation of the database system-specific extensions of the
- SQL <code>LIKE</code> operator. As a result, if you would like
- your application to be portable among various database systems,
- then limit the special characters used in the pattern to
- <code>%</code> (matches zero or more characters) and <code>_</code>
- (matches exactly one character). It is also possible to specify
- the escape character as a second argument to the <code>like()</code>
- function. This character can then be used to escape the special
- characters (<code>%</code> and <code>_</code>) in the pattern.
- For example, the following query will match any two characters
- separated by an underscore:</p>
-
- <pre class="cxx">
- query q (query::name.like ("_!__", "!"));
- </pre>
-
- <p>The operator precedence in the query expressions are the same
- as for equivalent C++ operators. We can use parentheses to
- make sure the expression is evaluated in the desired order.
- For example:</p>
-
- <pre class="cxx">
- query q ((query::first == "John" || query::first == "Jane") &amp;&amp;
- query::age &lt; 31);
- </pre>
-
-
- <h2><a name="4.2">4.2 Parameter Binding</a></h2>
-
- <p>An instance of the <code>odb::query</code> class encapsulates two
- parts of information about the query: the query expression and
- the query parameters. Parameters can be bound to C++ variables
- either by value or by reference.</p>
-
- <p>If a parameter is bound by value, then the value for this parameter
- is copied from the C++ variable to the query instance at the query
- construction time. On the other hand, if a parameter is bound by
- reference, then the query instance stores a reference to the
- bound variable. The actual value of the parameter is only extracted
- at the query execution time. Consider, for example, the following
- two queries:</p>
-
- <pre class="cxx">
- string name ("John");
-
- query q1 (query::first == query::_val (name));
- query q2 (query::first == query::_ref (name));
-
- name = "Jane";
-
- db.query&lt;person> (q1); // Find John.
- db.query&lt;person> (q2); // Find Jane.
- </pre>
-
- <p>The <code>odb::query</code> class provides two special functions,
- <code>_val()</code> and <code>_ref()</code>, that allow us to
- bind the parameter either by value or by reference, respectively.
- In the ODB query language, if the binding is not specified
- explicitly, the value semantic is used by default. In the
- native query language, binding must always be specified
- explicitly. For example:</p>
-
- <pre class="cxx">
- query q1 (query::age &lt; age); // By value.
- query q2 (query::age &lt; query::_val (age)); // By value.
- query q3 (query::age &lt; query::_ref (age)); // By reference.
-
- query q4 ("age &lt; " + age); // Error.
- query q5 ("age &lt; " + query::_val (age)); // By value.
- query q6 ("age &lt; " + query::_ref (age)); // By reference.
- </pre>
-
- <p>A query that only has by-value parameters does not depend on any
- other variables and is self-sufficient once constructed. A query
- that has one or more by-reference parameters depends on the
- bound variables until the query is executed. If one such variable
- goes out of scope and we execute the query, the behavior is
- undefined.</p>
-
- <h2><a name="4.3">4.3 Executing a Query</a></h2>
-
- <p>Once we have the query instance ready and by-reference parameters
- initialized, we can execute the query using the
- <code>database::query()</code> function template. It has two
- overloaded versions:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- result&lt;T>
- query (bool cache = true);
-
- template &lt;typename T>
- result&lt;T>
- query (const odb::query&lt;T>&amp;, bool cache = true);
- </pre>
-
- <p>The first <code>query()</code> function is used to return all the
- persistent objects of a given type stored in the database.
- The second function uses the passed query instance to only return
- objects matching the query criteria. The <code>cache</code> argument
- determines whether the objects' states should be cached in the
- application's memory or if they should be returned by the database
- system one by one as the iteration over the result progresses. The
- result caching is discussed in detail in the next section.</p>
-
- <p>When calling the <code>query()</code> function, we have to
- explicitly specify the object type we are querying. For example:</p>
-
- <pre class="cxx">
- typedef odb::query&lt;person> query;
- typedef odb::result&lt;person> result;
-
- result all (db.query&lt;person> ());
- result johns (db.query&lt;person> (query::first == "John"));
- </pre>
-
- <p>Note that it is not required to explicitly create a named
- query variable before executing it. For example, the following
- two queries are equivalent:</p>
-
- <pre class="cxx">
- query q (query::first == "John");
-
- result r1 (db.query&lt;person> (q));
- result r1 (db.query&lt;person> (query::first == "John"));
- </pre>
-
- <p>Normally, we would create a named query instance if we are
- planning to run the same query multiple times and would use the
- in-line version for those that are executed only once (see also
- <a href="#4.5">Section 4.5, "Prepared Queries"</a> for a more
- optimal way to re-execute the same query multiple times). A named
- query instance that does not have any by-reference parameters is
- immutable and can be shared between multiple threads without
- synchronization. On the other hand, a query instance with
- by-reference parameters is modified every time it is executed.
- If such a query is shared among multiple threads, then access
- to this query instance must be synchronized from the execution
- point and until the completion of the iteration over the result.</p>
-
- <p>It is also possible to create queries from other queries by
- combining them using logical operators. For example:</p>
-
- <pre class="cxx">
-result
-find_minors (database&amp; db, const query&amp; name_query)
-{
- return db.query&lt;person> (name_query &amp;&amp; query::age &lt; 18);
-}
-
-result r (find_minors (db, query::first == "John"));
- </pre>
-
- <p>The result of executing a query is zero, one, or more objects
- matching the query criteria. The <code>query()</code> function
- returns this result as an instance of the <code>odb::result</code>
- class template, which provides a stream-like interface and is
- discussed in detail in the next section.</p>
-
- <p>In situations where we know that a query produces at most one
- element, we can instead use the <code>database::query_one()</code> and
- <code>database::query_value()</code> shortcut functions, for example:</p>
-
- <pre class="cxx">
- typedef odb::query&lt;person> query;
-
- auto_ptr&lt;person> p (
- db.query_one&lt;person> (
- query::email == "jon@example.com"));
- </pre>
-
- <p>The shortcut query functions have the following signatures:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- typename object_traits&lt;T>::pointer_type
- query_one ();
-
- template &lt;typename T>
- bool
- query_one (T&amp;);
-
- template &lt;typename T>
- T
- query_value ();
-
- template &lt;typename T>
- typename object_traits&lt;T>::pointer_type
- query_one (const odb::query&lt;T>&amp;);
-
- template &lt;typename T>
- bool
- query_one (const odb::query&lt;T>&amp;, T&amp;);
-
- template &lt;typename T>
- T
- query_value (const odb::query&lt;T>&amp;);
- </pre>
-
- <p>Similar to <code>query()</code>, the first three functions are used
- to return the only persistent object of a given type stored in the
- database. The second three versions use the passed query instance
- to only return the object matching the query criteria.</p>
-
- <p>Similar to the <code>database::find()</code> functions
- (<a href="#3.9">Section 3.9, "Loading Persistent Objects"</a>),
- <code>query_one()</code> can either allocate a new instance of the
- object class in the dynamic memory or it can load the object's state
- into an existing instance. The <code>query_value()</code> function
- allocates and returns the object by value.</p>
-
- <p>The <code>query_one()</code> function allows us to determine
- if the query result contains zero or one element. If no objects
- matching the query criteria were found in the database, the
- first version of <code>query_one()</code> returns the <code>NULL</code>
- pointer while the second &mdash; <code>false</code>. If the second
- version returns <code>false</code>, then the passed object
- remains unchanged. For example:</p>
-
- <pre class="cxx">
- if (unique_ptr&lt;person> p = db.query_one&lt;person> (
- query::email == "jon@example.com"))
- {
- ...
- }
-
- person p;
- if (db.query_one&lt;person> (query::email == "jon@example.com", p))
- {
- ...
- }
- </pre>
-
- <p>If the query executed using <code>query_one()</code> or
- <code>query_value()</code> returns more than one element,
- then these functions fail with an assertion. Additionally,
- <code>query_value()</code> also fails with an assertion if
- the query returned no elements.</p>
-
- <p>Common situations where we can use the shortcut functions are a
- query condition that uses a data member with the
- <code>unique</code> constraint (at most one element returned;
- see <a href="#14.7">Section 14.7, "Index Definition Pragmas"</a>)
- as well as aggregate queries (exactly one element returned; see
- <a href="#10">Chapter 10, "Views"</a>).</p>
-
- <h2><a name="4.4">4.4 Query Result</a></h2>
-
- <p>The <code>database::query()</code> function returns the result of
- executing a query as an instance of the <code>odb::result</code>
- class template, for example:</p>
-
- <pre class="cxx">
- typedef odb::query&lt;person> query;
- typedef odb::result&lt;person> result;
-
- result johns (db.query&lt;person> (query::first == "John"));
- </pre>
-
- <p>It is best to view an instance of <code>odb::result</code>
- as a handle to a stream, such as a socket stream. While we can
- make a copy of a result or assign one result to another, the
- two instances will refer to the same result stream. Advancing
- the current position in one instance will also advance it in
- another. The result instance is only usable within the transaction
- it was created in. Trying to manipulate the result after the
- transaction has terminated leads to undefined behavior.</p>
-
- <p>The <code>odb::result</code> class template conforms to the
- standard C++ sequence requirements and has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- template &lt;typename T>
- class result
- {
- public:
- typedef odb::result_iterator&lt;T> iterator;
-
- public:
- result ();
-
- result (const result&amp;);
-
- result&amp;
- operator= (const result&amp;);
-
- void
- swap (result&amp;)
-
- public:
- iterator
- begin ();
-
- iterator
- end ();
-
- public:
- void
- cache ();
-
- bool
- empty () const;
-
- std::size_t
- size () const;
- };
-}
- </pre>
-
- <p>The default constructor creates an empty result set. The
- <code>cache()</code> function caches the returned objects'
- state in the application's memory. We have already mentioned
- result caching when we talked about query execution. As you
- may remember the <code>database::query()</code> function
- caches the result unless instructed not to by the caller.
- The <code>cache()</code> function allows us to
- cache the result at a later stage if it wasn't already
- cached during query execution.</p>
-
- <p>If the result is cached, the database state of all the returned
- objects is stored in the application's memory. Note that
- the actual objects are still only instantiated on demand
- during result iteration. It is the raw database state that
- is cached in memory. In contrast, for uncached results
- the object's state is sent by the database system one object
- at a time as the iteration progresses.</p>
-
- <p>Uncached results can improve the performance of both the application
- and the database system in situations where we have a large
- number of objects in the result or if we will only examine
- a small portion of the returned objects. However, uncached
- results have a number of limitations. There can only be one
- uncached result in a transaction. Creating another result
- (cached or uncached) by calling <code>database::query()</code>
- will invalidate the existing uncached result. Furthermore,
- calling any other database functions, such as <code>update()</code>
- or <code>erase()</code> will also invalidate the uncached result.
- It also follows that uncached results cannot be used on objects
- with containers (<a href="#5">Chapter 5, "Containers"</a>) since
- loading a container would invalidate the uncached result.</p>
-
- <p>The <code>empty()</code> function returns <code>true</code> if
- there are no objects in the result and <code>false</code> otherwise.
- The <code>size()</code> function can only be called for cached results.
- It returns the number of objects in the result. If we call this
- function on an uncached result, the <code>odb::result_not_cached</code>
- exception is thrown.</p>
-
- <p>To iterate over the objects in a result we use the
- <code>begin()</code> and <code>end()</code> functions
- together with the <code>odb::result&lt;T>::iterator</code>
- type, for example:</p>
-
- <pre class="cxx">
- result r (db.query&lt;person> (query::first == "John"));
-
- for (result::iterator i (r.begin ()); i != r.end (); ++i)
- {
- ...
- }
- </pre>
-
- <p>In C++11 we can use the <code>auto</code>-typed variabe instead
- of spelling the iterator type explicitly, for example:</p>
-
- <pre class="cxx">
- for (auto i (r.begin ()); i != r.end (); ++i)
- {
- ...
- }
- </pre>
-
- <p>The C++11 range-based <code>for</code>-loop can be used to further
- simplify the iteration:</p>
-
- <pre class="cxx">
- for (person&amp; p: r)
- {
- ...
- }
- </pre>
-
- <p>The result iterator is an input iterator which means that the
- only two position operations that it supports are to move to the
- next object and to determine whether the end of the result stream
- has been reached. In fact, the result iterator can only be in two
- states: the current position and the end position. If we have
- two iterators pointing to the current position and then we
- advance one of them, the other will advance as well. This,
- for example, means that it doesn't make sense to store an
- iterator that points to some object of interest in the result
- stream with the intent of dereferencing it after the iteration
- is over. Instead, we would need to store the object itself. We
- also cannot iterate over the same result multiple times without
- re-executing the query.</p>
-
- <p>The result iterator has the following dereference functions
- that can be used to access the pointed-to object:</p>
-
- <pre class="cxx">
-namespace odb
-{
- template &lt;typename T>
- class result_iterator
- {
- public:
- T*
- operator-> () const;
-
- T&amp;
- operator* () const;
-
- typename object_traits&lt;T>::pointer_type
- load ();
-
- void
- load (T&amp; x);
-
- typename object_traits&lt;T>::id_type
- id ();
- };
-}
- </pre>
-
- <p>When we call the <code>*</code> or <code>-></code> operator,
- the iterator will allocate a new instance of the object class
- in the dynamic memory, load its state from the database
- state, and return a reference or pointer to the new instance. The
- iterator maintains the ownership of the returned object and will
- return the same pointer for subsequent calls to either of these
- operators until it is advanced to the next object or we call
- the first <code>load()</code> function (see below). For example:</p>
-
- <pre class="cxx">
- result r (db.query&lt;person> (query::first == "John"));
-
- for (result::iterator i (r.begin ()); i != r.end ();)
- {
- cout &lt;&lt; i->last () &lt;&lt; endl; // Create an object.
- person&amp; p (*i); // Reference to the same object.
- cout &lt;&lt; p.age () &lt;&lt; endl;
- ++i; // Free the object.
- }
- </pre>
-
- <p>The overloaded <code>result_iterator::load()</code> functions are
- similar to <code>database::load()</code>. The first function
- returns a dynamically allocated instance of the current
- object. As an optimization, if the iterator already owns an object
- as a result of an earlier
- call to the <code>*</code> or <code>-></code> operator, then it
- relinquishes the ownership of this object and returns it instead.
- This allows us to write code like this without worrying about
- a double allocation:</p>
-
- <pre class="cxx">
- result r (db.query&lt;person> (query::first == "John"));
-
- for (result::iterator i (r.begin ()); i != r.end (); ++i)
- {
- if (i->last == "Doe")
- {
- auto_ptr p (i.load ());
- ...
- }
- }
- </pre>
-
- <p>Note, however, that because of this optimization, a subsequent
- to <code>load()</code> call to the <code>*</code> or <code>-></code>
- operator results in the allocation of a new object.</p>
-
- <p>The second <code>load()</code> function allows
- us to load the current object's state into an existing instance.
- For example:</p>
-
- <pre class="cxx">
- result r (db.query&lt;person> (query::first == "John"));
-
- person p;
- for (result::iterator i (r.begin ()); i != r.end (); ++i)
- {
- i.load (p);
- cout &lt;&lt; p.last () &lt;&lt; endl;
- cout &lt;&lt; i.age () &lt;&lt; endl;
- }
- </pre>
-
- <p>The <code>id()</code> function return the object id of the current
- object. While we can achieve the same by loading the object and getting
- its id, this function is more efficient since it doesn't actually
- create the object. This can be useful when all we need is the object's
- identifier. For example:</p>
-
- <pre class="cxx">
- std::set&lt;unsigned long> set = ...; // Persons of interest.
-
- result r (db.query&lt;person> (query::first == "John"));
-
- for (result::iterator i (r.begin ()); i != r.end (); ++i)
- {
- if (set.find (i.id ()) != set.end ()) // No object loaded.
- {
- cout &lt;&lt; i->first () &lt;&lt; endl; // Object loaded.
- }
- }
- </pre>
-
- <h2><a name="4.5">4.5 Prepared Queries</a></h2>
-
- <p>Most modern relational database systems have the notion of a prepared
- statement. Prepared statements allow us to perform the potentially
- expensive tasks of parsing SQL, preparing the query execution
- plan, etc., once and then executing the same query multiple
- times, potentially using different values for parameters in
- each execution.</p>
-
- <p>In ODB all the non-query database operations such as
- <code>persist()</code>, <code>load()</code>, <code>update()</code>,
- etc., are implemented in terms of prepared statements that are cached
- and reused. While the <code>query()</code>, <code>query_one()</code>,
- and <code>query_value()</code> database operations also use prepared
- statements, these statements are not cached or reused by default since
- ODB has no knowledge of whether a query will be executed multiple times
- or only once. Instead, ODB provides a mechanism, called prepared queries,
- that allows us to prepare a query once and execute it multiple
- times. In other words, ODB prepared queries are a thin wrapper
- around the underlying database's prepared statement functionality.</p>
-
- <p>In most cases ODB shields the application developer from database
- connection management and multi-threading issues. However, when it
- comes to prepared queries, a basic understanding of how ODB manages
- these aspects is required. Conceptually, the <code>odb::database</code>
- class represents a specific database, that is, a data store. However,
- underneath, it maintains one or more connections to this database.
- A connection can be used only by a single thread at a time. When
- we start a transaction (by calling <code>database::begin()</code>),
- the transaction instance obtains a connection and holds on to it
- until the transaction is committed or rolled back. During this time
- no other thread can use this connection. When the transaction
- releases the connection, it may be closed or reused by another
- transaction in this or another thread. What exactly happens to
- a connection after it has been released depends on the connection
- factory that is used by the <code>odb::database</code> instance.
- For more information on connection factories, refer to
- <a href="#II">Part II, "Database Systems"</a>.</p>
-
- <p>A query prepared on one connection cannot be executed on another.
- In other words, a prepared query is associated with the connection.
- One important implication of this restriction is that we cannot
- prepare a query in one transaction and then try to execute it
- in another without making sure that both transactions use the
- same connection.</p>
-
- <p>To enable the prepared query functionality we need to specify
- the <code>--generate-prepared</code> ODB compiler option. If
- we are planning to always prepare our queries, then we can
- disable the once-off query execution support by also specifying
- the <code>--omit-unprepared</code> option.</p>
-
- <p>To prepare a query we use the <code>prepare_query()</code> function
- template. This function can be called on both the <code>odb::database</code>
- and <code>odb::connection</code> instances. The <code>odb::database</code>
- version simply obtains the connection used by the currently active
- transaction and calls the corresponding <code>odb::connection</code>
- version. If no transaction is currently active, then this function
- throws the <code>odb::not_in_transaction</code> exception
- (<a href="#3.5">Section 3.5, "Transactions"</a>). The
- <code>prepare_query()</code> function has the following signature:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- prepared_query&lt;T>
- prepare_query (const char* name, const odb::query&lt;T>&amp;);
- </pre>
-
- <p>The first argument to the <code>prepare_query()</code> function is
- the prepared query name. This name is used as a key for prepared
- query caching (discussed later) and must be unique. For some databases,
- notably PostgreSQL, it is also used as a name of the underlying prepared
- statement. The name <code>"<i>object</i>_query"</code> (for example,
- <code>"person_query"</code>) is reserved for the once-off queries
- executed by the <code>database::query()</code> function. Note that
- the <code>prepare_query()</code> function makes only a shallow copy
- of this argument, which means that the name must be valid for the
- lifetime of the returned <code>prepared_query</code> instance.</p>
-
- <p>The second argument to the <code>prepare_query()</code> function
- is the query criteria. It has the same semantics as in the
- <code>query()</code> function discussed in <a href="#4.3">Section
- 4.3, "Executing a Query"</a>. Similar to <code>query()</code>, we
- also have to explicitly specify the object type that we will be
- querying. For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;person> query;
-typedef odb::prepared_query&lt;person> prep_query;
-
-prep_query pq (
- db.prepare_query&lt;person> ("person-age-query", query::age > 50));
- </pre>
-
- <p>The result of executing the <code>prepare_query()</code> function is
- the <code>prepared_query</code> instance that represent the prepared
- query. It is best to view <code>prepared_query</code> as a handle to
- the underlying prepared statement. While we can make a copy of it or
- assign one <code>prepared_query</code> to another, the two instances
- will refer to the same prepared statement. Once the last instance of
- <code>prepared_query</code> referencing a specific prepared statement
- is destroyed, this statement is released. The <code>prepared_query</code>
- class template has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- template &lt;typename T>
- struct prepared_query
- {
- prepared_query ();
-
- prepared_query (const prepared_query&amp;)
- prepared_query&amp; operator= (const prepared_query&amp;)
-
- result&lt;T>
- execute (bool cache = true);
-
- typename object_traits&lt;T>::pointer_type
- execute_one ();
-
- bool
- execute_one (T&amp; object);
-
- T
- execute_value ();
-
- const char*
- name () const;
-
- statement&amp;
- statement () const;
-
- operator unspecified_bool_type () const;
- };
-}
- </pre>
-
- <p>The default constructor creates an empty <code>prepared_query</code>
- instance, that is, an instance that does not reference a prepared
- statement and therefore cannot be executed. The only way to create
- a non-empty prepared query is by calling the <code>prepare_query()</code>
- function discussed above. To test whether the prepared query is empty,
- we can use the implicit conversion operator to a boolean type. For
- example:</p>
-
- <pre class="cxx">
- prepared_query&lt;person> pq;
-
- if (pq)
- {
- // Not empty.
- ...
- }
- </pre>
-
- <p>The <code>execute()</code> function executes the query and returns
- the result instance. The <code>cache</code> argument indicates
- whether the result should be cached and has the same semantics
- as in the <code>query()</code> function. In fact, conceptually,
- <code>prepare_query()</code> and <code>execute()</code> are just
- the <code>query()</code> function split into two:
- <code>prepare_query()</code> takes the first
- <code>query()</code> argument (the query condition) while
- <code>execute()</code> takes the second (the cache flag). Note
- also that re-executing a prepared query invalidates the
- previous execution result, whether cached or uncached. </p>
-
- <p>The <code>execute_one()</code> and <code>execute_value()</code>
- functions can be used as shortcuts to execute a query that is
- known to return at most one or exactly one object, respectively.
- The arguments and return values in these functions have the same
- semantics as in <code>query_one()</code> and <code>query_value()</code>.
- And similar to <code>execute()</code> above, <code>prepare_query()</code>
- and <code>execute_one/value()</code> can be seen as the
- <code>query_one/value()</code> function split into two:
- <code>prepare_query()</code> takes the first
- <code>query_one/value()</code> argument (the query condition) while
- <code>execute_one/value()</code> takes the second argument (if any)
- and returns the result. Note also that <code>execute_one/value()</code>
- never caches its result but invalidates the result of any previous
- <code>execute()</code> call on the same prepared query.</p>
-
- <p>The <code>name()</code> function returns the prepared query name.
- This is the same name as was passed as the first argument in the
- <code>prepare_query()</code> call. The <code>statement()</code>
- function returns a reference to the underlying prepared statement.
- Note also that calling any of these functions on an empty
- <code>prepared_query</code> instance results in undefined behavior.</p>
-
- <p>The simplest use-case for a prepared query is the need to
- execute the same query multiple times within a single transaction.
- Consider the following example that queries for people that are older
- than a number of different ages. This and subsequent code fragments
- are taken from the <code>prepared</code> example in the
- <code>odb-examples</code> package.</p>
-
- <pre class="cxx">
-typedef odb::query&lt;person> query;
-typedef odb::prepared_query&lt;person> prep_query;
-typedef odb::result&lt;person> result;
-
-transaction t (db.begin ());
-
-unsigned short age;
-query q (query::age > query::_ref (age));
-prep_query pq (db.prepare_query&lt;person> ("person-age-query", q));
-
-for (age = 90; age > 40; age -= 10)
-{
- result r (pq.execute ());
- ...
-}
-
-t.commit ();
- </pre>
-
- <p>Another scenario is the need to reuse the same query in multiple
- transactions that are executed at once. As was mentioned above,
- in this case we need to make sure that the prepared query and
- all the transactions use the same connection. Consider an
- alternative version of the above example that executes each
- query in a separate transaction:</p>
-
- <pre class="cxx">
-connection_ptr conn (db.connection ());
-
-unsigned short age;
-query q (query::age > query::_ref (age));
-prep_query pq (conn->prepare_query&lt;person> ("person-age-query", q));
-
-for (age = 90; age > 40; age -= 10)
-{
- transaction t (conn->begin ());
-
- result r (pq.execute ());
- ...
-
- t.commit ();
-}
- </pre>
-
-
- <p>Note that with this approach we hold on to the database connection
- until all the transactions involving the prepared query are
- executed. In particular, this means that while we are busy, the
- connection cannot be reused by another thread. Therefore, this
- approach is only recommended if all the transactions are executed
- close to each other. Also note that an uncached (see below)
- prepared query is invalidated once we release the connection
- on which it was prepared.</p>
-
- <p>If we need to reuse a prepared query in transactions that are
- executed at various times, potentially in different threads, then
- the recommended approach is to cache the prepared query on the
- connection. To support this functionality the <code>odb::database</code>
- and <code>odb::connection</code> classes provide the following
- function templates. Similar to <code>prepare_query()</code>,
- the <code>odb::database</code> versions of the below
- functions call the corresponding <code>odb::connection</code>
- versions using the currently active transaction to resolve
- the connection.</p>
-
- <pre class="cxx">
- template &lt;typename T>
- void
- cache_query (const prepared_query&lt;T>&amp;);
-
- template &lt;typename T, typename P>
- void
- cache_query (const prepared_query&lt;T>&amp;,
- std::[auto|unique]_ptr&lt;P> params);
-
- template &lt;typename T>
- prepared_query&lt;T>
- lookup_query (const char* name);
-
- template &lt;typename T, typename P>
- prepared_query&lt;T>
- lookup_query (const char* name, P*&amp; params);
- </pre>
-
- <p>The <code>cache_query()</code> function caches the passed prepared
- query on the connection. The second overloaded version of
- <code>cache_query()</code> also takes a pointer to the
- by-reference query parameters. In C++98/03 it should be
- <code>std::auto_ptr</code> while in C++11 <code>std::auto_ptr</code>
- or <code>std::unique_ptr</code> can be used. The
- <code>cache_query()</code> function assumes ownership of the
- passed <code>params</code> argument. If a prepared query
- with the same name is already cached on this connection,
- then the <code>odb::prepared_already_cached</code> exception
- is thrown.</p>
-
- <p>The <code>lookup_query()</code> function looks up a previously
- cached prepared query given its name. The second overloaded
- version of <code>lookup_query()</code> also returns a pointer
- to the by-reference query parameters. If a prepared query
- with this name has not been cached, then an empty
- <code>prepared_query</code> instance is returned. If a
- prepared query with this name has been cached but either
- the object type or the parameters type does not match
- that which was cached, then the <code>odb::prepared_type_mismatch</code>
- exception is thrown.</p>
-
- <p>As a first example of the prepared query cache functionality,
- consider the case that does not use any by-reference parameters:</p>
-
- <pre class="cxx">
-for (unsigned short i (0); i &lt; 5; ++i)
-{
- transaction t (db.begin ());
-
- prep_query pq (db.lookup_query&lt;person> ("person-age-query"));
-
- if (!pq)
- {
- pq = db.prepare_query&lt;person> (
- "person-val-age-query", query::age > 50);
- db.cache_query (pq);
- }
-
- result r (pq.execute ());
- ...
-
- t.commit ();
-
- // Do some other work.
- //
- ...
-}
- </pre>
-
- <p>The following example shows how to do the same but for a query that
- includes by-reference parameters. In this case the parameters are
- cached together with the prepared query.</p>
-
- <pre class="cxx">
-for (unsigned short age (90); age > 40; age -= 10)
-{
- transaction t (db.begin ());
-
- unsigned short* age_param;
- prep_query pq (
- db.lookup_query&lt;person> ("person-age-query", age_param));
-
- if (!pq)
- {
- auto_ptr&lt;unsigned short> p (new unsigned short);
- age_param = p.get ();
- query q (query::age > query::_ref (*age_param));
- pq = db.prepare_query&lt;person> ("person-age-query", q);
- db.cache_query (pq, p); // Assumes ownership of p.
- }
-
- *age_param = age; // Initialize the parameter.
- result r (pq.execute ());
- ...
-
- t.commit ();
-
- // Do some other work.
- //
- ...
-}
- </pre>
-
- <p>As is evident from the above examples, when we use a prepared
- query cache, each transaction that executes a query must also
- include code that prepares and caches this query if it hasn't already
- been done. If a prepared query is used in a single place in the
- application, then this is normally not an issue since all the
- relevant code is kept in one place. However, if the same query
- is used in several different places in the application, then
- we may end up duplicating the same preparation and caching
- code, which makes it hard to maintain.</p>
-
- <p>To resolve this issue ODB allows us to register a prepared
- query factory that will be called to prepare and cache a
- query during the call to <code>lookup_query()</code>. To
- register a factory we use the <code>database::query_factory()</code>
- function. In C++98/03 it has the following signature:</p>
-
- <pre class="cxx">
- void
- query_factory (const char* name,
- void (*factory) (const char* name, connection&amp;));
- </pre>
-
- <p>While in C++11 it uses the <code>std::function</code> class
- template:</p>
-
- <pre class="cxx">
- void
- query_factory (const char* name,
- std::function&lt;void (const char* name, connection&amp;)>);
- </pre>
-
- <p>The first argument to the <code>query_factory()</code> function is
- the prepared query name that this factory will be called to prepare
- and cache. An empty name is treated as a fallback wildcard factory
- that is capable of preparing any query. The second argument is the
- factory function or, in C++11, function object or lambda.</p>
-
- <p>The example fragment shows how we can use the prepared query
- factory:</p>
-
- <pre class="cxx">
-struct params
-{
- unsigned short age;
- string first;
-};
-
-static void
-query_factory (const char* name, connection&amp; c)
-{
- auto_ptr&lt;params> p (new params);
- query q (query::age > query::_ref (p->age) &amp;&amp;
- query::first == query::_ref (p->first));
- prep_query pq (c.prepare_query&lt;person> (name, q));
- c.cache_query (pq, p);
-}
-
-db.query_factory ("person-age-name-query", &amp;query_factory);
-
-for (unsigned short age (90); age > 40; age -= 10)
-{
- transaction t (db.begin ());
-
- params* p;
- prep_query pq (db.lookup_query&lt;person> ("person-age-name-query", p));
- assert (pq);
-
- p->age = age;
- p->first = "John";
- result r (pq.execute ());
- ...
-
- t.commit ();
-}
- </pre>
-
- <p>In C++11 we could have instead used a lambda function as well as
- <code>unique_ptr</code> rather than <code>auto_ptr</code>:</p>
-
- <pre>
-db.query_factory (
- "person-age-name-query",
- [] (const char* name, connection&amp; c)
- {
- unique_ptr&lt;params> p (new params);
- query q (query::age > query::_ref (p->age) &amp;&amp;
- query::first == query::_ref (p->first));
- prep_query pq (c.prepare_query&lt;person> (name, q));
- c.cache_query (pq, std::move (p));
- });
- </pre>
-
- <!-- CHAPTER -->
-
- <hr class="page-break"/>
- <h1><a name="5">5 Containers</a></h1>
-
- <p>The ODB runtime library provides built-in persistence support for all the
- commonly used standard C++98/03 containers, namely,
- <code>std::vector</code>, <code>std::list</code>, <code>std::deque</code>,
- <code>std::set</code>, <code>std::multiset</code>, <code>std::map</code>, and
- <code>std::multimap</code> as well as C++11 <code>std::array</code>,
- <code>std::forward_list</code>, <code>std::unordered_set</code>,
- <code>std::unordered_multiset</code>, <code>std::unordered_map</code>,
- and <code>std::unordered_multimap</code>.
- Plus, ODB profile libraries, that are
- available for commonly used frameworks and libraries (such as Boost and
- Qt), provide persistence support for containers found in these frameworks
- and libraries (<a href="#III">Part III, "Profiles"</a>). Both the
- ODB runtime library and profile libraries also provide a number of
- change-tracking container equivalents which can be used to minimize
- the number of database operations necessary to synchronize the container
- state with the database (<a href="#5.4">Section 5.4, "Change-Tracking
- Containers"</a>). It is also easy to persist custom container types
- as discussed later in <a href="#5.5">Section 5.5, "Using Custom
- Containers"</a>.</p>
-
- <p>We don't need to do anything special to declare a member of a
- container type in a persistent class. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- std::vector&lt;std::string> nicknames_;
- ...
-};
- </pre>
-
- <p>The complete version of the above code fragment and the other code
- samples presented in this chapter can be found in the <code>container</code>
- example in the <code>odb-examples</code> package.</p>
-
- <p>A data member in a persistent class that is of a container type
- behaves like a value type. That is, when an object is made persistent,
- the elements of the container are stored in the database. Similarly,
- when a persistent object is loaded from the database, the contents
- of the container are automatically loaded as well. A data member
- of a container type can also use a smart pointer, as discussed
- in <a href="#7.3">Section 7.3, "Pointers and <code>NULL</code>
- Value Semantics"</a>.</p>
-
- <p>While an ordinary member is mapped to one or more columns in the
- object's table, a member of a container type is mapped to a separate
- table. The exact schema of such a table depends on the kind of
- container. ODB defines the following container kinds: ordered,
- set, multiset, map, and multimap. The container kinds and the
- contents of the tables to which they are mapped are discussed
- in detail in the following sections.</p>
-
- <p>Containers in ODB can contain simple value types (<a href="#7.1">Section
- 7.1, "Simple Value Types"</a>), composite value types
- (<a href="#7.2">Section 7.2, "Composite Value Types"</a>), and pointers
- to objects (<a href="#6">Chapter 6, "Relationships"</a>). Containers of
- containers, either directly or indirectly via a composite value
- type, are not allowed. A key in a map or multimap container can
- be a simple or composite value type but not a pointer to an object.
- An index in the ordered container should be a simple integer value
- type.</p>
-
- <p>The value type in the ordered, set, and map containers as well as
- the key type in the map containers should be default-constructible.
- The default constructor in these types can be made private in which
- case the <code>odb::access</code> class should be made a friend of
- the value or key type. For example:</p>
-
- <pre class="cxx">
-#pragma db value
-class name
-{
-public:
- name (const std::string&amp;, const std::string&amp;);
- ...
-private:
- friend class odb::access;
- name ();
- ...
-};
-
-#pragma db object
-class person
-{
- ...
-private:
- std::vector&lt;name> aliases_;
- ...
-};
- </pre>
-
-
- <h2><a name="5.1">5.1 Ordered Containers</a></h2>
-
- <p>In ODB an ordered container is any container that maintains (explicitly
- or implicitly) an order of its elements in the form of an integer index.
- Standard C++ containers that are ordered include <code>std::vector</code>
- <code>std::list</code>, and <code>std::deque</code> as well as C++11 <code>std::array</code> and
- <code>std::forward_list</code>. While elements in <code>std::set</code>
- are also kept in a specific order, this order is not based on an
- integer index but rather on the relationship between elements. As
- a result, <code>std::set</code> is not considered an ordered
- container for the purpose of persistence.</p>
-
- <p>The database table for an ordered container consists of at least
- three columns. The first column contains the object id of a
- persistent class instance of which the container is a member.
- The second column contains the element index within a container.
- And the last column contains the element value. If the object
- id or element value are composite, then, instead of a single
- column, they can occupy multiple columns. For an ordered
- container table the ODB compiler also defines two indexes:
- one for the object id column(s) and the other for the index
- column. Refer to <a href="#14.7">Section 14.7, "Index Definition
- Pragmas"</a> for more information on how to customize these
- indexes.</p>
-
- <p>Consider the following persistent object as an example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- #pragma db id auto
- unsigned long id_;
-
- std::vector&lt;std::string> nicknames_;
- ...
-};
- </pre>
-
- <p>The resulting database table (called <code>person_nicknames</code>) will
- contain the object id column of type <code>unsigned&nbsp;long</code>
- (called <code>object_id</code>), the index column of an integer type
- (called <code>index</code>), and the value column of type
- <code>std::string</code> (called <code>value</code>).</p>
-
- <p>A number of ODB pragmas allow us to customize the table name, column
- names, and native database types of an ordered container both, on
- the per-container and per-member basis. For more information on
- these pragmas, refer to <a href="#14">Chapter 14, "ODB Pragma
- Language"</a>. The following example shows some of the possible
- customizations:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- #pragma db table("nicknames") \
- id_column("person_id") \
- index_type("SMALLINT UNSIGNED") \
- index_column("nickname_number") \
- value_type("VARCHAR(255)") \
- value_column("nickname")
- std::vector&lt;std::string> nicknames_;
- ...
-};
- </pre>
-
- <p>While the C++ container used in a persistent class may be ordered,
- sometimes we may wish to store such a container in the database without
- the order information. In the example above, for instance, the order
- of person's nicknames is probably not important. To instruct the ODB
- compiler to ignore the order in ordered containers we can use the
- <code>db&nbsp;unordered</code> pragma (<a href="#14.3.9">Section 14.3.9,
- "<code>unordered</code>"</a>, <a href="#14.4.19">Section 14.4.19,
- "<code>unordered</code>"</a>). For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- #pragma db unordered
- std::vector&lt;std::string> nicknames_;
- ...
-};
- </pre>
-
- <p>The table for an ordered container that is marked unordered won't
- have the index column and the order in which elements are retrieved
- from the database may not be the same as the order in which they
- were stored.</p>
-
- <h2><a name="5.2">5.2 Set and Multiset Containers</a></h2>
-
- <p>In ODB set and multiset containers (referred to as just set
- containers) are associative containers that contain elements
- based on some relationship between them. A set container may
- or may not guarantee a particular order of the elements that
- it stores. Standard C++ containers that are considered set
- containers for the purpose of persistence include
- <code>std::set</code> and <code>std::multiset</code> as well
- as C++11 <code>std::unordered_set</code> and
- <code>std::unordered_multiset</code>.</p>
-
- <p>The database table for a set container consists of at least
- two columns. The first column contains the object id of a
- persistent class instance of which the container is a member.
- And the second column contains the element value. If the object
- id or element value are composite, then, instead of a single
- column, they can occupy multiple columns. ODB compiler also
- defines an index on a set container table for the object id
- column(s). Refer to <a href="#14.7">Section 14.7, "Index Definition
- Pragmas"</a> for more information on how to customize this
- index.</p>
-
- <p>Consider the following persistent object as an example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- #pragma db id auto
- unsigned long id_;
-
- std::set&lt;std::string> emails_;
- ...
-};
- </pre>
-
- <p>The resulting database table (called <code>person_emails</code>) will
- contain the object id column of type <code>unsigned&nbsp;long</code>
- (called <code>object_id</code>) and the value column of type
- <code>std::string</code> (called <code>value</code>).</p>
-
- <p>A number of ODB pragmas allow us to customize the table name,
- column names, and native database types of a set container, both on
- the per-container and per-member basis. For more information on
- these pragmas, refer to <a href="#14">Chapter 14, "ODB Pragma
- Language"</a>. The following example shows some of the possible
- customizations:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- #pragma db table("emails") \
- id_column("person_id") \
- value_type("VARCHAR(255)") \
- value_column("email")
- std::set&lt;std::string> emails_;
- ...
-};
- </pre>
-
- <h2><a name="5.3">5.3 Map and Multimap Containers</a></h2>
-
- <p>In ODB map and multimap containers (referred to as just map
- containers) are associative containers that contain key-value
- elements based on some relationship between keys. A map container
- may or may not guarantee a particular order of the elements that
- it stores. Standard C++ containers that are considered map
- containers for the purpose of persistence include
- <code>std::map</code> and <code>std::multimap</code> as well
- as C++11 <code>std::unordered_map</code> and
- <code>std::unordered_multimap</code>.</p>
-
- <p>The database table for a map container consists of at least
- three columns. The first column contains the object id of a
- persistent class instance of which the container is a member.
- The second column contains the element key. And the last column
- contains the element value. If the object id, element key, or
- element value are composite, then instead of a single column
- they can occupy multiple columns. ODB compiler also
- defines an index on a map container table for the object id
- column(s). Refer to <a href="#14.7">Section 14.7, "Index Definition
- Pragmas"</a> for more information on how to customize this
- index.</p>
-
- <p>Consider the following persistent object as an example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- #pragma db id auto
- unsigned long id_;
-
- std::map&lt;unsigned short, float> age_weight_map_;
- ...
-};
- </pre>
-
- <p>The resulting database table (called <code>person_age_weight_map</code>)
- will contain the object id column of type <code>unsigned&nbsp;long</code>
- (called <code>object_id</code>), the key column of type
- <code>unsigned short</code> (called <code>key</code>), and the value
- column of type <code>float</code> (called <code>value</code>).</p>
-
- <p>A number of ODB pragmas allow us to customize the table name,
- column names, and native database types of a map container, both on
- the per-container and per-member basis. For more information on
- these pragmas, refer to <a href="#14">Chapter 14, "ODB Pragma
- Language"</a>. The following example shows some of the possible
- customizations:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- #pragma db table("weight_map") \
- id_column("person_id") \
- key_type("INT UNSIGNED") \
- key_column("age") \
- value_type("DOUBLE") \
- value_column("weight")
- std::map&lt;unsigned short, float> age_weight_map_;
- ...
-};
- </pre>
-
- <h2><a name="5.4">5.4 Change-Tracking Containers</a></h2>
-
- <p>When a persistent object containing one of the standard containers
- is updated in the database, ODB has no knowledge of which elements
- were inserted, erased, or modified. As a result, ODB has no choice
- but to assume the whole container has changed and update the state
- of every single element. This can result in a significant overhead
- if a container contains a large number of elements and we only
- changed a small subset of them.</p>
-
- <p>To eliminate this overhead, ODB provides a notion of <em>change-tracking
- containers</em>. A change-tracking container, besides containing
- its elements, just like an ordinary container, also includes the
- change state for each element. When it is time to update such a
- container in the database, ODB can use this change information to
- perform a minimum number of database operations necessary to
- synchronize the container state with the database.</p>
-
- <p>The current version of the ODB runtime library provides a change-tracking
- equivalent of <code>std::vector</code> (<a href="#5.4.1">Section 5.4.1,
- "Change-Tracking <code>vector</code>"</a>) with support for other
- standard container equivalents planned for future releases. ODB
- profile libraries also provide change-tracking equivalents for some
- containers found in the corresponding frameworks and libraries
- (<a href="#III">Part III, "Profiles"</a>).</p>
-
- <p>A change-tracking container equivalent can normally be used as a drop-in
- replacement for an ordinary container except for a few minor
- interface differences (discussed in the corresponding sub-sections).
- In particular, we don't need to do anything extra to effect
- change tracking. ODB will automatically start, stop, and reset
- change tracking when necessary. The following example illustrates
- this point using <code>odb::vector</code> as a replacement for
- <code>std::vector</code>.</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- odb::vector&lt;std::string> names;
-};
-
-person p; // No change tracking (not persistent).
-p.names.push_back ("John Doe");
-
-{
- transaction t (db.begin ());
- db.persist (p); // Start change tracking (persistent).
- t.commit ();
-}
-
-p.names.push_back ("Johnny Doo");
-
-{
- transaction t (db.begin ());
- db.update (p); // One INSERT; reset change state.
- t.commit ();
-}
-
-p.names.modify (0) = "Doe, John"; // Instead of operator[].
-p.names.pop_back ();
-
-{
- transaction t (db.begin ());
- db.update (p); // One UPDATE, one DELETE; reset change state.
- t.commit ();
-}
-
-{
- transaction t (db.begin ());
- auto_ptr&lt;person> p1 (db.load&lt;person> (...)); // Start change tracking.
- p1->names.insert (p1->names.begin (), "Joe Do");
- db.update (*p1); // One UPDATE, one INSERT; reset change state.
- t.commit ();
-}
-
-{
- transaction t (db.begin ());
- db.erase (p); // One DELETE; stop change tracking (not persistent).
- t.commit ();
-}
- </pre>
-
- <p>One interesting aspect of change tracking is what happens when a
- transaction that contains an update is later rolled back. In this
- case, while the change-tracking container has reset the change
- state (after update), actual changes were not committed to the
- database. Change-tracking containers handle this case by
- automatically registering a rollback callback and then, if it is
- called, marking the container as "completely changed". In this
- state, the container no longer tracks individual element changes
- and, when updated, falls back to the complete state update, just
- like an ordinary container. The following example illustrates
- this point:</p>
-
- <pre class="cxx">
-person p;
-p.names.push_back ("John Doe");
-
-{
- transaction t (db.begin ());
- db.persist (p); // Start change tracking (persistent).
- t.commit ();
-}
-
-p.names.push_back ("Johnny Doo");
-
-for (;;)
-{
- try
- {
- transaction t (db.begin ());
-
- // First try: one INSERT.
- // Next try: one DELETE, two INSERTs.
- //
- db.update (p); // Reset change state.
-
- t.commit (); // If throws (rollback), mark as completely changed.
- break;
- }
- catch (const odb::recoverable&amp;)
- {
- continue;
- }
-}
- </pre>
-
- <p>For the interaction of change-tracking containers with change-updated
- object sections, refer to <a href="#9.4">Section 9.4, "Sections and
- Change-Tracking Containers"</a>. Note also that change-tracking
- containers cannot be accessed with by-value accessors
- (<a href="#14.4.5">Section 14.4.5,
- "<code>get</code>/<code>set</code>/<code>access</code>"</a>)
- since in certain situations such access may involve a
- modification of the container (for example, clearing the change
- flag after update).</p>
-
- <h3><a name="5.4.1">5.4.1 Change-Tracking <code>vector</code></a></h3>
-
- <p>Class template <code>odb::vector</code>, defined in
- <code>&lt;odb/vector.hxx></code>, is a change-tracking
- equivalent for <code>std::vector</code>. It
- is implemented in terms of <code>std::vector</code> and is
- implicit-convertible to and implicit-constructible from
- <code>const std::vector&amp;</code>. In particular, this
- means that we can use <code>odb::vector</code> instance
- anywhere <code>const std::vector&amp;</code> is
- expected. In addition, <code>odb::vector</code> constant
- iterator (<code>const_iterator</code>) is the same type as
- that of <code>std::vector</code>.</p>
-
- <p><code>odb::vector</code> incurs 2-bit per element overhead
- in order to store the change state. It cannot
- be stored unordered in the database (<a href="#14.4.19">Section
- 14.4.19 "<code>unordered</code>"</a>) but can be used as an inverse
- side of a relationship (<a href="#6.2">6.2 "Bidirectional
- Relationships"</a>). In this case, no change tracking is performed
- since no state for such a container is stored in the database.</p>
-
- <p>The number of database operations required to update the state
- of <code>odb::vector</code> corresponds well to the complexity
- of <code>std::vector</code> functions. In particular, adding or
- removing an element from the back of the vector (for example,
- with <code>push_back()</code> and <code>pop_back()</code>),
- requires only a single database statement execution. In contrast,
- inserting or erasing an element somewhere in the middle of the
- vector will require a database statement for every element that
- follows it.</p>
-
- <p><code>odb::vector</code> replicates most of the <code>std::vector</code>
- interface as defined in both C++98/03 and C++11 standards. However,
- functions and operators that provide direct write access to
- the elements had to be altered or disabled in order to support
- change tracking. Additional functions used to interface with
- <code>std::vector</code> and to control the change tracking state
- were also added. The following listing summarizes the differences
- between the <code>odb::vector</code> and <code>std::vector</code>
- interfaces. Any <code>std::vector</code> function or operator
- not mentioned in this listing has exactly the same signature
- and semantics in <code>odb::vector</code>. Functions and
- operators that were disabled are shown as commented out and
- are followed by functions/operators that replace them.</p>
-
- <pre class="cxx">
-namespace odb
-{
- template &lt;class T, class A = std::allocator&lt;T> >
- class vector
- {
- ...
-
- // Element access.
- //
-
- //reference operator[] (size_type);
- reference modify (size_type);
-
- //reference at (size_type);
- reference modify_at (size_type);
-
- //reference front ();
- reference modify_front ();
-
- //reference back ();
- reference modify_back ();
-
- //T* data () noexcept;
- T* modify_data () noexcept; // C++11 only.
-
- // Iterators.
- //
- typedef typename std::vector&lt;T, A>::const_iterator const_iterator;
-
- class iterator
- {
- ...
-
- // Element Access.
- //
-
- //reference operator* () const;
- const_reference operator* () const;
- reference modify () const;
-
- //pointer operator-> () const;
- const_pointer operator-> () const;
-
- //reference operator[] (difference_type);
- const_reference operator[] (difference_type);
- reference modify (difference_type) const;
-
- // Interfacing with std::vector::iterator.
- //
- typename std::vector&lt;T, A>::iterator base () const;
- };
-
- // Return std::vector iterators. The begin() functions mark
- // all the elements as modified.
- //
- typename std::vector&lt;T, A>::iterator mbegin ();
- typename std::vector&lt;T, A>::iterator mend ();
- typename std::vector&lt;T, A>::reverse_iterator mrbegin ();
- typename std::vector&lt;T, A>::reverse_iterator mrend ();
-
- // Interfacing with std::vector.
- //
- vector (const std::vector&lt;T, A>&amp;);
- vector (std::vector&lt;T, A>&amp;&amp;); // C++11 only.
-
- vector&amp; operator= (const std::vector&lt;T, A>&amp;);
- vector&amp; operator= (std::vector&lt;T, A>&amp;&amp;); // C++11 only.
-
- operator const std::vector&lt;T, A>&amp; () const;
- std::vector&lt;T, A>&amp; base ();
- const std::vector&lt;T, A>&amp; base ();
-
- // Change tracking.
- //
- bool _tracking () const;
- void _start () const;
- void _stop () const;
- void _arm (transaction&amp;) const;
- };
-}
- </pre>
-
- <p>The following example highlights some of the differences between
- the two interfaces. <code>std::vector</code> versions are commented
- out.</p>
-
- <pre class="cxx">
-#include &lt;vector>
-#include &lt;odb/vector.hxx>
-
-void f (const std::vector&lt;int>&amp;);
-
-odb::vector&lt;int> v ({1, 2, 3});
-
-f (v); // Ok, implicit conversion.
-
-if (v[1] == 2) // Ok, const access.
- //v[1]++;
- v.modify (1)++;
-
-//v.back () = 4;
-v.modify_back () = 4;
-
-for (auto i (v.begin ()); i != v.end (); ++i)
-{
- if (*i != 0) // Ok, const access.
- //*i += 10;
- i.modify () += 10;
-}
-
-std::sort (v.mbegin (), v.mend ());
- </pre>
-
- <p>Note also the subtle difference between copy/move construction
- and copy/move assignment of <code>odb::vector</code> instances.
- While copy/move constructor will copy/move both the elements as
- well as their change state, in contrast, assignment is tracked
- as any other change to the vector content.</p>
-
- <h2><a name="5.5">5.5 Using Custom Containers</a></h2>
-
- <p>While the ODB runtime and profile libraries provide support for
- a wide range of containers, it is also easy to persist custom
- container types or make a change-tracking version out of one.</p>
-
- <p>To achieve this you will need to implement the
- <code>container_traits</code> class template specialization for
- your container. First, determine the container kind (ordered, set,
- multiset, map, or multimap) for your container type. Then use a
- specialization for one of the standard C++ containers found in
- the common ODB runtime library (<code>libodb</code>) as a base
- for your own implementation.</p>
-
- <p>Once the container traits specialization is ready for your container,
- you will need to include it into the ODB compilation process using
- the <code>--odb-epilogue</code> option and into the generated header
- files with the <code>--hxx-prologue</code> option. As an example,
- suppose we have a hash table container for which we have the traits
- specialization implemented in the <code>hashtable-traits.hxx</code>
- file. Then, we can create an ODB compiler options file for this
- container and save it to <code>hashtable.options</code>:</p>
-
- <pre>
-# Options file for the hash table container.
-#
---odb-epilogue '#include "hashtable-traits.hxx"'
---hxx-prologue '#include "hashtable-traits.hxx"'
- </pre>
-
- <p>Now, whenever we compile a header file that uses the hashtable
- container, we can specify the following command line option to
- make sure it is recognized by the ODB compiler as a container
- and the traits file is included in the generated code:</p>
-
- <pre>
---options-file hashtable.options
- </pre>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="6">6 Relationships</a></h1>
-
- <p>Relationships between persistent objects are expressed with pointers or
- containers of pointers. The ODB runtime library provides built-in support
- for <code>shared_ptr</code>/<code>weak_ptr</code> (TR1 or C++11),
- <code>std::unique_ptr</code> (C++11),
- <code>std::auto_ptr</code>, and raw pointers. Plus, ODB profile
- libraries, that are available for commonly used frameworks and libraries
- (such as Boost and Qt), provide support for smart pointers found in these
- frameworks and libraries (<a href="#III">Part III, "Profiles"</a>). It is
- also easy to add support for a custom smart pointer as discussed later
- in <a href="#6.5"> Section 6.5, "Using Custom Smart Pointers"</a>. Any
- supported smart pointer can be used in a data member as long as it can be
- explicitly constructed from the canonical object pointer
- (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>). For
- example, we can use <code>weak_ptr</code> if the object pointer
- is <code>shared_ptr</code>.</p>
-
- <p>When an object containing a pointer to another object is loaded,
- the pointed-to object is loaded as well. In some situations this
- eager loading of the relationships is undesirable since it
- can lead to a large number of otherwise unused objects being
- instantiated from the database. To support finer control
- over relationships loading, the ODB runtime and profile
- libraries provide the so-called <em>lazy</em> versions of
- the supported pointers. An object pointed-to by a lazy pointer
- is not loaded automatically when the containing object is loaded.
- Instead, we have to explicitly request the instantiation of the
- pointed-to object. Lazy pointers are discussed in
- detail in <a href="#6.4">Section 6.4, "Lazy Pointers"</a>.</p>
-
- <p>As a simple example, consider the following employee-employer
- relationship. Code examples presented in this chapter
- will use the <code>shared_ptr</code> and <code>weak_ptr</code>
- smart pointers from the TR1 (<code>std::tr1</code>) namespace.</p>
-
- <pre class="cxx">
-#pragma db object
-class employer
-{
- ...
-
- #pragma db id
- std::string name_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- std::string first_name_;
- std::string last_name_;
-
- shared_ptr&lt;employer> employer_;
-};
- </pre>
-
- <p>By default, an object pointer can be <code>NULL</code>. To
- specify that a pointer always points to a valid object we can
- use the <code>not_null</code> pragma (<a href="#14.4.6">Section
- 14.4.6, "<code>null</code>/<code>not_null</code>"</a>) for
- single object pointers and the <code>value_not_null</code> pragma
- (<a href="#14.4.28">Section
- 14.4.28, "<code>value_null</code>/<code>value_not_null</code>"</a>)
- for containers of object pointers. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class employee
-{
- ...
-
- #pragma db not_null
- shared_ptr&lt;employer> current_employer_;
-
- #pragma db value_not_null
- std::vector&lt;shared_ptr&lt;employer> > previous_employers_;
-};
- </pre>
-
- <p>In this case, if we call either <code>persist()</code> or
- <code>update()</code> database function on the
- <code>employee</code> object and the <code>current_employer_</code>
- pointer or one of the pointers stored in the
- <code>previous_employers_</code> container is <code>NULL</code>,
- then the <code>odb::null_pointer</code> exception will be thrown.</p>
-
- <p>We don't need to do anything special to establish or navigate a
- relationship between two persistent objects, as shown in the
- following code fragment:</p>
-
- <pre class="cxx">
-// Create an employer and a few employees.
-//
-unsigned long john_id, jane_id;
-{
- shared_ptr&lt;employer> er (new employer ("Example Inc"));
- shared_ptr&lt;employee> john (new employee ("John", "Doe"));
- shared_ptr&lt;employee> jane (new employee ("Jane", "Doe"));
-
- john->employer_ = er;
- jane->employer_ = er;
-
- transaction t (db.begin ());
-
- db.persist (er);
- john_id = db.persist (john);
- jane_id = db.persist (jane);
-
- t.commit ();
-}
-
-// Load a few employee objects and print their employer.
-//
-{
- session s;
- transaction t (db.begin ());
-
- shared_ptr&lt;employee> john (db.load&lt;employee> (john_id));
- shared_ptr&lt;employee> jane (db.load&lt;employee> (jane_id));
-
- cout &lt;&lt; john->employer_->name_ &lt;&lt; endl;
- cout &lt;&lt; jane->employer_->name_ &lt;&lt; endl;
-
- t.commit ();
-}
- </pre>
-
- <p>The only notable line in the above code is the creation of a
- session before the second transaction starts. As discussed in
- <a href="#11">Chapter 11, "Session"</a>, a session acts as a cache
- of persistent objects.
- By creating a session before loading the <code>employee</code>
- objects we make sure that their <code>employer_</code> pointers
- point to the same <code>employer</code> object. Without a
- session, each <code>employee</code> would have ended up pointing
- to its own, private instance of the Example Inc employer.</p>
-
- <p>As a general guideline, you should use a session when loading
- objects that have pointers to other persistent objects. A
- session makes sure that for a given object id, a single instance
- is shared among all other objects that relate to it.</p>
-
- <p>We can also use data members from pointed-to
- objects in database queries (<a href="#4">Chapter 4, "Querying the
- Database"</a>). For each pointer in a persistent class, the query
- class defines a smart pointer-like member that contains members
- corresponding to the data members in the pointed-to object. We
- can then use the access via a pointer syntax (<code>-></code>)
- to refer to data members in pointed-to objects.
- For example, the query class for the <code>employee</code> object
- contains the <code>employer</code> member (its name is derived from the
- <code>employer_</code> pointer) which in turn contains the
- <code>name</code> member (its name is derived from the
- <code>employer::name_</code> data member of the pointed-to object).
- As a result, we can use the <code>query::employer->name</code>
- expression while querying the database for the <code>employee</code>
- objects. For example, the following transaction finds all the
- employees of Example Inc that have the Doe last name:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee> query;
-typedef odb::result&lt;employee> result;
-
-session s;
-transaction t (db.begin ());
-
-result r (db.query&lt;employee> (
- query::employer->name == "Example Inc" &amp;&amp; query::last == "Doe"));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
- cout &lt;&lt; i->first_ &lt;&lt; " " &lt;&lt; i->last_ &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>A query class member corresponding to a non-inverse
- (<a href="#6.2">Section 6.2, "Bidirectional Relationships"</a>) object
- pointer can also be used as a normal member that has the id type
- of the pointed-to object. For example, the following query locates
- all the <code>employee</code> objects that don't have an associated
- <code>employer</code> object:</p>
-
- <pre class="cxx">
-result r (db.query&lt;employee> (query::employer.is_null ()));
- </pre>
-
- <p>An important concept to keep in mind when working with object
- relationships is the independence of persistent objects. In particular,
- when an object containing a pointer to another object is made persistent
- or is updated, the pointed-to object is not automatically persisted
- or updated. Rather, only a reference to the object (in the form of the
- object id) is stored for the pointed-to object in the database.
- The pointed-to object itself is a separate entity and should
- be made persistent or updated independently. By default, the
- same principle also applies to erasing pointed-to objects. That
- is, we have to make sure all the pointing objects are updated
- accordingly. However, in the case of erase, we can specify an
- alternative <code>on-delete</code> semantic as discussed in
- <a href="#14.4.15">Section 14.4.15, "<code>on_delete</code>"</a>.</p>
-
- <p>When persisting or updating an object containing a pointer to another
- object, the pointed-to object must have a valid object id. This,
- however, may not always be easy to achieve in complex relationships that
- involve objects with automatically assigned identifiers. In such
- cases it may be necessary to first persist an object with a pointer
- set to <code>NULL</code> and then, once the pointed-to object is
- made persistent and its identifier assigned, set the pointer
- to the correct value and update the object in the database.</p>
-
- <p>Persistent object relationships can be divided into two groups:
- unidirectional and bidirectional. Each group in turn contains
- several configurations that vary depending on the cardinality
- of the sides of the relationship. All possible unidirectional
- and bidirectional configurations are discussed in the following
- sections.</p>
-
- <h2><a name="6.1">6.1 Unidirectional Relationships</a></h2>
-
- <p>In unidirectional relationships we are only interested in navigating
- from object to object in one direction. Because there is no interest
- in navigating in the opposite direction, the cardinality of the other
- end of the relationship is unimportant. As a result, there are only
- two possible unidirectional relationships: to-one and to-many. Each
- of these relationships is described in the following sections. For
- sample code that shows how to work with these relationships, refer
- to the <code>relationship</code> example in the <code>odb-examples</code>
- package.</p>
-
- <h3><a name="6.1.1">6.1.1 To-One Relationships</a></h3>
-
- <p>An example of a unidirectional to-one relationship is the
- employee-employer relationship (an employee has one employer).
- The following persistent C++ classes model this relationship:</p>
-
- <pre class="cxx">
-#pragma db object
-class employer
-{
- ...
-
- #pragma db id
- std::string name_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db not_null
- shared_ptr&lt;employer> employer_;
-};
- </pre>
-
- <p>The corresponding database tables look like this:</p>
-
- <pre class="sql">
-CREATE TABLE employer (
- name VARCHAR (128) NOT NULL PRIMARY KEY);
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- employer VARCHAR (128) NOT NULL REFERENCES employer (name));
- </pre>
-
- <h3><a name="6.1.2">6.1.2 To-Many Relationships</a></h3>
-
- <p>An example of a unidirectional to-many relationship is the
- employee-project relationship (an employee can be involved
- in multiple projects). The following persistent C++ classes
- model this relationship:</p>
-
- <pre class="cxx">
-#pragma db object
-class project
-{
- ...
-
- #pragma db id
- std::string name_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db value_not_null unordered
- std::vector&lt;shared_ptr&lt;project> > projects_;
-};
- </pre>
-
- <p>The corresponding database tables look like this:</p>
-
- <pre class="sql">
-CREATE TABLE project (
- name VARCHAR (128) NOT NULL PRIMARY KEY);
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
-
-CREATE TABLE employee_projects (
- object_id BIGINT UNSIGNED NOT NULL,
- value VARCHAR (128) NOT NULL REFERENCES project (name));
- </pre>
-
- <p>To obtain a more canonical database schema, the names of tables
- and columns above can be customized using ODB pragmas
- (<a href="#14">Chapter 14, "ODB Pragma Language"</a>). For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class employee
-{
- ...
-
- #pragma db value_not_null unordered \
- id_column("employee_id") value_column("project_name")
- std::vector&lt;shared_ptr&lt;project> > projects_;
-};
- </pre>
-
- <p>The resulting <code>employee_projects</code> table would then
- look like this:</p>
-
- <pre class="sql">
-CREATE TABLE employee_projects (
- employee_id BIGINT UNSIGNED NOT NULL,
- project_name VARCHAR (128) NOT NULL REFERENCES project (name));
- </pre>
-
-
- <h2><a name="6.2">6.2 Bidirectional Relationships</a></h2>
-
- <p>In bidirectional relationships we are interested in navigating
- from object to object in both directions. As a result, each
- object class in a relationship contains a pointer to the other
- object. If smart pointers are used, then a weak pointer should
- be used as one of the pointers to avoid ownership cycles. For
- example:</p>
-
- <pre class="cxx">
-class employee;
-
-#pragma db object
-class position
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- weak_ptr&lt;employee> employee_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db not_null
- shared_ptr&lt;position> position_;
-};
- </pre>
-
- <p>Note that when we establish a bidirectional relationship, we
- have to set both pointers consistently. One way to make sure
- that a relationship is always in a consistent state is to
- provide a single function that updates both pointers at the
- same time. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class position: public enable_shared_from_this&lt;position>
-{
- ...
-
- void
- fill (shared_ptr&lt;employee> e)
- {
- employee_ = e;
- e->positions_ = shared_from_this ();
- }
-
-private:
- weak_ptr&lt;employee> employee_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
-private:
- friend class position;
-
- #pragma db not_null
- shared_ptr&lt;position> position_;
-};
- </pre>
-
- <p>At the beginning of this chapter we examined how to use a session
- to make sure a single object is shared among all other objects pointing
- to it. With bidirectional relationships involving weak pointers the
- use of a session becomes even more crucial. Consider the following
- transaction that tries to load the <code>position</code> object
- from the above example without using a session:</p>
-
- <pre class="cxx">
-transaction t (db.begin ())
-shared_ptr&lt;position> p (db.load&lt;position> (1));
-...
-t.commit ();
- </pre>
-
- <p>When we load the <code>position</code> object, the <code>employee</code>
- object, which it points to, is also loaded. While <code>employee</code>
- is initially stored as <code>shared_ptr</code>, it is then assigned to
- the <code>employee_</code> member which is <code>weak_ptr</code>. Once
- the assignment is complete, the shared pointer goes out of scope
- and the only pointer that points to the newly loaded
- <code>employee</code> object is the <code>employee_</code> weak
- pointer. And that means the <code>employee</code> object is deleted
- immediately after being loaded. To help avoid such pathological
- situations ODB detects cases where a newly loaded object will
- immediately be deleted and throws the <code>odb::session_required</code>
- exception.</p>
-
- <p>As the exception name suggests, the easiest way to resolve this
- problem is to use a session:</p>
-
- <pre class="cxx">
-session s;
-transaction t (db.begin ())
-shared_ptr&lt;position> p (db.load&lt;position> (1));
-...
-t.commit ();
- </pre>
-
- <p>In our example, the session will maintain a shared pointer to the
- loaded <code>employee</code> object preventing its immediate
- deletion. Another way to resolve this problem is to avoid
- immediate loading of the pointed-to objects using lazy weak
- pointers. Lazy pointers are discussed in <a href="#6.4">Section 6.4,
- "Lazy Pointers"</a> later in this chapter.</p>
-
- <p>Above, to model a bidirectional relationship in persistent classes,
- we used two pointers, one in each object. While this is a natural
- representation in C++, it does not translate to a canonical
- relational model. Consider the database schema generated for
- the above two classes:</p>
-
- <pre class="sql">
-CREATE TABLE position (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- employee BIGINT UNSIGNED REFERENCES employee (id));
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- position BIGINT UNSIGNED NOT NULL REFERENCES position (id));
- </pre>
-
- <p>While this database schema is valid, it is unconventional. We have
- a reference from a row in the <code>position</code> table to a row
- in the <code>employee</code> table. We also have a reference
- from this same row in the <code>employee</code> table back to
- the row in the <code>position</code> table. From the relational
- point of view, one of these references is redundant since
- in SQL we can easily navigate in both directions using just one
- of these references.</p>
-
- <p>To eliminate redundant database schema references we can use the
- <code>inverse</code> pragma (<a href="#14.4.14">Section 14.4.14,
- "<code>inverse</code>"</a>) which tells the ODB compiler that
- a pointer is the inverse side of a bidirectional relationship.
- Either side of a relationship can be made inverse. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class position
-{
- ...
-
- #pragma db inverse(position_)
- weak_ptr&lt;employee> employee_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db not_null
- shared_ptr&lt;position> position_;
-};
- </pre>
-
- <p>The resulting database schema looks like this:</p>
-
- <pre class="sql">
-CREATE TABLE position (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- position BIGINT UNSIGNED NOT NULL REFERENCES position (id));
- </pre>
-
- <p>As you can see, an inverse member does not have a corresponding
- column (or table, in case of an inverse container of pointers)
- and, from the point of view of database operations, is effectively
- read-only. The only way to change a bidirectional relationship
- with an inverse side is to set its direct (non-inverse)
- pointer. Also note that an ordered container (<a href="#5.1">Section
- 5.1, "Ordered Containers"</a>) of pointers that is an inverse side
- of a bidirectional relationship is always treated as unordered
- (<a href="#14.4.19">Section 14.4.19, "<code>unordered</code>"</a>)
- because the contents of such a container are implicitly built from
- the direct side of the relationship which does not contain the
- element order (index).</p>
-
- <p>There are three distinct bidirectional relationships that we
- will cover in the following sections: one-to-one, one-to-many,
- and many-to-many. We will only talk about bidirectional
- relationships with inverse sides since they result in canonical
- database schemas. For sample code that shows how to work with
- these relationships, refer to the <code>inverse</code> example
- in the <code>odb-examples</code> package.</p>
-
- <h3><a name="6.2.1">6.2.1 One-to-One Relationships</a></h3>
-
- <p>An example of a bidirectional one-to-one relationship is the
- presented above employee-position relationship (an employee
- fills one position and a position is filled by one employee).
- The following persistent C++ classes model this relationship:</p>
-
- <pre class="cxx">
-class employee;
-
-#pragma db object
-class position
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db inverse(position_)
- weak_ptr&lt;employee> employee_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db not_null
- shared_ptr&lt;position> position_;
-};
- </pre>
-
- <p>The corresponding database tables look like this:</p>
-
- <pre class="sql">
-CREATE TABLE position (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- position BIGINT UNSIGNED NOT NULL REFERENCES position (id));
- </pre>
-
- <p>If instead the other side of this relationship is made inverse,
- then the database tables will change as follows:</p>
-
- <pre class="sql">
-CREATE TABLE position (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- employee BIGINT UNSIGNED REFERENCES employee (id));
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
- </pre>
-
- <h3><a name="6.2.2">6.2.2 One-to-Many Relationships</a></h3>
-
- <p>An example of a bidirectional one-to-many relationship is the
- employer-employee relationship (an employer has multiple
- employees and an employee is employed by one employer).
- The following persistent C++ classes model this relationship:</p>
-
- <pre class="cxx">
-class employee;
-
-#pragma db object
-class employer
-{
- ...
-
- #pragma db id
- std::string name_;
-
- #pragma db value_not_null inverse(employer_)
- std::vector&lt;weak_ptr&lt;employee> > employees_
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db not_null
- shared_ptr&lt;employer> employer_;
-};
- </pre>
-
- <p>The corresponding database tables differ significantly depending
- on which side of the relationship is made inverse. If the <em>one</em>
- side (<code>employer</code>) is inverse as in the code
- above, then the resulting database schema looks like this:</p>
-
- <pre class="sql">
-CREATE TABLE employer (
- name VARCHAR (128) NOT NULL PRIMARY KEY);
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- employer VARCHAR (128) NOT NULL REFERENCES employer (name));
- </pre>
-
- <p>If instead the <em>many</em> side (<code>employee</code>) of this
- relationship is made inverse, then the database tables will change
- as follows:</p>
-
- <pre class="sql">
-CREATE TABLE employer (
- name VARCHAR (128) NOT NULL PRIMARY KEY);
-
-CREATE TABLE employer_employees (
- object_id VARCHAR (128) NOT NULL REFERENCES employer (name),
- value BIGINT UNSIGNED NOT NULL REFERENCES employee (id));
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
- </pre>
-
- <h3><a name="6.2.3">6.2.3 Many-to-Many Relationships</a></h3>
-
- <p>An example of a bidirectional many-to-many relationship is the
- employee-project relationship (an employee can work on multiple
- projects and a project can have multiple participating employees).
- The following persistent C++ classes model this relationship:</p>
-
- <pre class="cxx">
-class employee;
-
-#pragma db object
-class project
-{
- ...
-
- #pragma db id
- std::string name_;
-
- #pragma db value_not_null inverse(projects_)
- std::vector&lt;weak_ptr&lt;employee> > employees_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db value_not_null unordered
- std::vector&lt;shared_ptr&lt;project> > projects_;
-};
- </pre>
-
- <p>The corresponding database tables look like this:</p>
-
- <pre class="sql">
-CREATE TABLE project (
- name VARCHAR (128) NOT NULL PRIMARY KEY);
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
-
-CREATE TABLE employee_projects (
- object_id BIGINT UNSIGNED NOT NULL REFERENCES employee (id),
- value VARCHAR (128) NOT NULL REFERENCES project (name));
- </pre>
-
- <p>If instead the other side of this relationship is made inverse,
- then the database tables will change as follows:</p>
-
- <pre class="sql">
-CREATE TABLE project (
- name VARCHAR (128) NOT NULL PRIMARY KEY);
-
-CREATE TABLE project_employees (
- object_id VARCHAR (128) NOT NULL REFERENCES project (name),
- value BIGINT UNSIGNED NOT NULL REFERENCES employee (id));
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
- </pre>
-
- <h2><a name="6.3">6.3 Circular Relationships</a></h2>
-
- <p>A relationship between two persistent classes is circular if each
- of them references the other. Bidirectional relationships are
- always circular. A unidirectional relationship combined with
- inheritance (<a href="#8">Chapter 8, "Inheritance"</a>) can also
- be circular. For example, the <code>employee</code> class could
- derive from <code>person</code> which, in turn, could contain a
- pointer to <code>employee</code>.</p>
-
- <p>We don't need to do anything extra if persistent classes with
- circular dependencies are defined in the same header
- file. Specifically, ODB will make sure that the database tables
- and foreign key constraints are created in the correct order. As a
- result, unless you have good reasons not to, it is recommended that
- you keep persistent classes with circular dependencies in the same
- header file.</p>
-
- <p>If you have to keep such classes in separate header files, then
- there are two extra steps that you may need to take in order to
- use these classes with ODB. Consider again the example from
- <a href="#6.2.1">Section 6.2.1, "One-to-One Relationships"</a>
- but this time with the classes defined in separate headers:</p>
-
- <pre class="cxx">
-// position.hxx
-//
-class employee;
-
-#pragma db object
-class position
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db inverse(position_)
- weak_ptr&lt;employee> employee_;
-};
- </pre>
-
- <pre class="cxx">
-// employee.hxx
-//
-#include "position.hxx"
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db not_null
- shared_ptr&lt;position> position_;
-};
- </pre>
-
- <p>Note that the <code>position.hxx</code> header contains only the forward
- declaration for <code>employee</code>. While this is sufficient to
- define a valid, from the C++ point of view, <code>position</code> class,
- the ODB compiler needs to "see" the definitions of the pointed-to
- persistent classes. There are several ways we can fulfil this
- requirement. The easiest is to simply include <code>employee.hxx</code>
- at the end of <code>position.hxx</code>:</p>
-
- <pre class="cxx">
-// position.hxx
-//
-class employee;
-
-#pragma db object
-class position
-{
- ...
-};
-
-#include "employee.hxx"
- </pre>
-
- <p>We can also limit this inclusion only to the time when
- <code>position.hxx</code> is compiled with the ODB compiler:</p>
-
- <pre class="cxx">
-// position.hxx
-//
-
-...
-
-#ifdef ODB_COMPILER
-# include "employee.hxx"
-#endif
- </pre>
-
- <p>Finally, if we don't want to modify <code>position.hxx</code>,
- then we can add <code>employee.hxx</code> to the ODB compilation
- process with the <code>--odb-epilogue</code> option. For example:</p>
-
- <pre class="terminal">
-odb ... --odb-epilogue "#include \"employee.hxx\"" position.hxx
- </pre>
-
- <p>Note also that in this example we didn't have to do anything extra
- for <code>employee.hxx</code> because it already includes
- <code>position.hxx</code>. However, if instead it relied only
- on the forward declaration of the <code>position</code> class,
- then we would have to handle it in the same way as
- <code>position.hxx</code>.</p>
-
- <p>The other difficulty with separately defined classes involving
- circular relationships has to do with the correct order of foreign
- key constraint creation in the generated database schema. In
- the above example, if we generate the database schema as
- standalone SQL files, then we will end up with two such files:
- <code>position.sql</code> and <code>employee.sql</code>.
- If we try to execute <code>employee.sql</code> first, then
- we will get an error indicating that the table corresponding to
- the <code>position</code> class and referenced by the foreign
- key constraint corresponding to the <code>position_</code>
- pointer does not yet exist.</p>
-
- <p>Note that there is no such problem if the database schema
- is embedded in the generated C++ code instead of being produced
- as standalone SQL files. In this case, the ODB compiler is
- able to ensure the correct creation order even if the classes
- are defined in separate header files.</p>
-
- <p>In certain cases, for example, a bidirectional relationship
- with an inverse side, this problem can be resolved by executing
- the database schema creation files in the correct order. In our
- example, this would be <code>position.sql</code> first
- and <code>employee.sql</code> second. However, this approach
- doesn't scale beyond simple object models.</p>
-
- <p>A more robust solution to this problem is to generate the database
- schema for all the persistent classes into a single SQL file. This
- way, the ODB compiler can again ensure the correct creation order
- of tables and foreign keys. To instruct the ODB compiler to produce
- a combined schema file for several headers we can use the
- <code>--generate-schema-only</code> and <code>--at-once</code>
- options. For example:</p>
-
- <pre class="terminal">
-odb ... --generate-schema-only --at-once --input-name company \
-position.hxx employee.hxx
- </pre>
-
- <p>The result of the above command is a single <code>company.sql</code>
- file (the name is derived from the <code>--input-name</code> value)
- that contains the database creation code for both <code>position</code>
- and <code>employee</code> classes.</p>
-
- <h2><a name="6.4">6.4 Lazy Pointers</a></h2>
-
- <p>Consider again the bidirectional, one-to-many employer-employee
- relationship that was presented earlier in this chapter:</p>
-
- <pre class="cxx">
-class employee;
-
-#pragma db object
-class employer
-{
- ...
-
- #pragma db id
- std::string name_;
-
- #pragma db value_not_null inverse(employer_)
- std::vector&lt;weak_ptr&lt;employee> > employees_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db not_null
- shared_ptr&lt;employer> employer_;
-};
- </pre>
-
- <p>Consider also the following transaction which obtains the employer
- name given the employee id:</p>
-
- <pre class="cxx">
-unsigned long id = ...
-string name;
-
-session s;
-transaction t (db.begin ());
-
-shared_ptr&lt;employee> e (db.load&lt;employee> (id));
-name = e->employer_->name_;
-
-t.commit ();
- </pre>
-
- <p>While this transaction looks very simple, it actually does a lot more
- than what meets the eye and is necessary. Consider what happens when
- we load the <code>employee</code> object: the <code>employer_</code>
- pointer is also automatically loaded which means the <code>employer</code>
- object corresponding to this employee is also loaded. But the
- <code>employer</code> object in turn contains the list of pointers
- to all the employees, which are also loaded. As a result, when object
- relationships are involved, a simple transaction like the above can
- load many more objects than is necessary.</p>
-
- <p>To overcome this problem ODB offers finer grained control over
- the relationship loading in the form of lazy pointers. A lazy
- pointer does not automatically load the pointed-to object
- when the containing object is loaded. Instead, we have to
- explicitly load the pointed-to object if and when we need to
- access it.</p>
-
- <p>The ODB runtime library provides lazy counterparts for all the
- supported pointers, namely:
- <code>odb::lazy_shared_ptr</code>/<code>lazy_weak_ptr</code>
- for C++11 <code>std::shared_ptr</code>/<code>weak_ptr</code>,
- <code>odb::tr1::lazy_shared_ptr</code>/<code>lazy_weak_ptr</code>
- for TR1 <code>std::tr1::shared_ptr</code>/<code>weak_ptr</code>,
- <code>odb::lazy_unique_ptr</code> for C++11 <code>std::unique_ptr</code>,
- <code>odb::lazy_auto_ptr</code> for <code>std::auto_ptr</code>,
- and <code>odb::lazy_ptr</code> for raw pointers. The TR1 lazy
- pointers are defined in the <code>&lt;odb/tr1/lazy-ptr.hxx></code>
- header while all the others &mdash; in
- <code>&lt;odb/lazy-ptr.hxx></code>. The ODB profile
- libraries also provide lazy pointer implementations for smart pointers
- from popular frameworks and libraries (<a href="#III">Part III,
- "Profiles"</a>).</p>
-
- <p>While we will discuss the interface of lazy pointers in more detail
- shortly, the most commonly used extra function provided by these
- pointers is <code>load()</code>. This function loads the
- pointed-to object if it hasn't already been loaded. After
- the call to this function, the lazy pointer can be used
- in the the same way as its eager counterpart. The <code>load()</code>
- function also returns the eager pointer, in case you need to pass
- it around. For a lazy weak pointer, the
- <code>load()</code> function also locks the pointer.</p>
-
- <p>The following example shows how we can change our employer-employee
- relationship to use lazy pointers. Here we choose to use lazy pointers
- for both sides of the relationship.</p>
-
- <pre class="cxx">
-class employee;
-
-#pragma db object
-class employer
-{
- ...
-
- #pragma db value_not_null inverse(employer_)
- std::vector&lt;lazy_weak_ptr&lt;employee> > employees_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db not_null
- lazy_shared_ptr&lt;employer> employer_;
-};
- </pre>
-
- <p>And the transaction is changed like this:</p>
-
- <pre class="cxx">
-unsigned long id = ...
-string name;
-
-session s;
-transaction t (db.begin ());
-
-shared_ptr&lt;employee> e (db.load&lt;employee> (id));
-e->employer_.load ();
-name = e->employer_->name_;
-
-t.commit ();
- </pre>
-
-
- <p>As a general guideline we recommend that you make at least one side
- of a bidirectional relationship lazy, especially for relationships
- with a <em>many</em> side.</p>
-
- <p>A lazy pointer implementation mimics the interface of its eager
- counterpart which can be used once the pointer is loaded. It also
- adds a number of additional functions that are specific to the
- lazy loading functionality. Overall, the interface of a lazy
- pointer follows this general outline:</p>
-
- <pre class="cxx">
-template &lt;class T>
-class lazy_ptr
-{
-public:
- //
- // The eager pointer interface.
- //
-
- // Initialization/assignment from an eager pointer to a
- // transient object.
- //
-public:
- template &lt;class Y> lazy_ptr (const eager_ptr&lt;Y>&amp;);
- template &lt;class Y> lazy_ptr&amp; operator= (const eager_ptr&lt;Y>&amp;);
-
- // Lazy loading interface.
- //
-public:
- // NULL loaded()
- //
- // true true NULL pointer to transient object
- // false true valid pointer to persistent object
- // true false unloaded pointer to persistent object
- // false false valid pointer to transient object
- //
- bool loaded () const;
-
- eager_ptr&lt;T> load () const;
-
- // Unload the pointer. For transient objects this function is
- // equivalent to reset().
- //
- void unload () const;
-
- // Get the underlying eager pointer. If this is an unloaded pointer
- // to a persistent object, then the returned pointer will be NULL.
- //
- eager_ptr&lt;T> get_eager () const;
-
- // Initialization with a persistent loaded object.
- //
- template &lt;class Y> lazy_ptr (database&amp;, Y*);
- template &lt;class Y> lazy_ptr (database&amp;, const eager_ptr&lt;Y>&amp;);
-
- template &lt;class Y> void reset (database&amp;, Y*);
- template &lt;class Y> void reset (database&amp;, const eager_ptr&lt;Y>&amp;);
-
- // Initialization with a persistent unloaded object.
- //
- template &lt;class ID> lazy_ptr (database&amp;, const ID&amp;);
-
- template &lt;class ID> void reset (database&amp;, const ID&amp;);
-
- // Query object id and database of a persistent object.
- //
- template &lt;class O /* = T */>
- // C++11: template &lt;class O = T>
- object_traits&lt;O>::id_type object_id () const;
-
- odb::database&amp; database () const;
-};
- </pre>
-
- <p>Note that to initialize a lazy pointer to a persistent object from
- its eager pointer one must use the constructor or <code>reset()</code>
- function with the database as its first argument. The database is
- required to support comparison of unloaded lazy pointers to persistent
- objects.</p>
-
- <p>In a lazy weak pointer interface, the <code>load()</code> function
- returns the <em>strong</em> (shared) eager pointer. The following
- transaction demonstrates the use of a lazy weak pointer based on
- the <code>employer</code> and <code>employee</code> classes
- presented earlier.</p>
-
- <pre class="cxx">
-typedef std::vector&lt;lazy_weak_ptr&lt;employee> > employees;
-
-session s;
-transaction t (db.begin ());
-
-shared_ptr&lt;employer> er (db.load&lt;employer> ("Example Inc"));
-employees&amp; es (er->employees ());
-
-for (employees::iterator i (es.begin ()); i != es.end (); ++i)
-{
- // We are only interested in employees with object id less than
- // 100.
- //
- lazy_weak_ptr&lt;employee>&amp; lwp (*i);
-
- if (lwp.object_id&lt;employee> () &lt; 100)
- // C++11: if (lwp.object_id () &lt; 100)
- {
- shared_ptr&lt;employee> e (lwp.load ()); // Load and lock.
- cout &lt;&lt; e->first_ &lt;&lt; " " &lt;&lt; e->last_ &lt;&lt; endl;
- }
-}
-
-t.commit ();
- </pre>
-
- <p>Notice that inside the for-loop we use a reference to the lazy
- weak pointer instead of making a copy. This is not merely to
- avoid a copy. When a lazy pointer is loaded, all other lazy
- pointers that point to the same object do not automatically
- become loaded (though an attempt to load such copies will
- result in them pointing to the same object, provided the
- same session is still in effect). By using a reference
- in the above transaction we make sure that we load the
- pointer that is contained in the <code>employer</code>
- object. This way, if we later need to re-examine this
- <code>employee</code> object, the pointer will already
- be loaded.</p>
-
- <p>As another example, suppose we want to add an employee
- to Example Inc. The straightforward implementation of this
- transaction is presented below:</p>
-
- <pre class="cxx">
-session s;
-transaction t (db.begin ());
-
-shared_ptr&lt;employer> er (db.load&lt;employer> ("Example Inc"));
-shared_ptr&lt;employee> e (new employee ("John", "Doe"));
-
-e->employer_ = er;
-er->employees ().push_back (e);
-
-db.persist (e);
-t.commit ();
- </pre>
-
- <p>Notice here that we didn't have to update the employer object
- in the database since the <code>employees_</code> list of
- pointers is an inverse side of a bidirectional relationship
- and is effectively read-only, from the persistence point of
- view.</p>
-
- <p>A faster implementation of this transaction, that avoids loading
- the employer object, relies on the ability to initialize an
- <em>unloaded</em> lazy pointer with the database where the object
- is stored as well as its identifier:</p>
-
- <pre class="cxx">
-lazy_shared_ptr&lt;employer> er (db, std::string ("Example Inc"));
-shared_ptr&lt;employee> e (new employee ("John", "Doe"));
-
-e->employer_ = er;
-
-session s;
-transaction t (db.begin ());
-
-db.persist (e);
-
-t.commit ();
- </pre>
-
- <p>For the interaction of lazy pointers with lazy-loaded object
- sections, refer to <a href="#9.3">Section 9.3, "Sections and
- Lazy Pointers"</a>.</p>
-
- <h2><a name="6.5">6.5 Using Custom Smart Pointers</a></h2>
-
- <p>While the ODB runtime and profile libraries provide support for
- the majority of widely-used pointers, it is also easy to add
- support for a custom smart pointer.</p>
-
- <p>To achieve this you will need to implement the
- <code>pointer_traits</code> class template specialization for
- your pointer. The first step is to determine the pointer kind
- since the interface of the <code>pointer_traits</code> specialization
- varies depending on the pointer kind. The supported pointer kinds
- are: <em>raw</em> (raw pointer or equivalent, that is, unmanaged),
- <em>unique</em> (smart pointer that doesn't support sharing),
- <em>shared</em> (smart pointer that supports sharing), and
- <em>weak</em> (weak counterpart of the shared pointer). Any of
- these pointers can be lazy, which also affects the
- interface of the <code>pointer_traits</code> specialization.</p>
-
- <p>Once you have determined the pointer kind for your smart pointer,
- use a specialization for one of the standard pointers found in
- the common ODB runtime library (<code>libodb</code>) as a base
- for your own implementation.</p>
-
- <p>Once the pointer traits specialization is ready, you will need to
- include it into the ODB compilation process using the
- <code>--odb-epilogue</code> option and into the generated header
- files with the <code>--hxx-prologue</code> option. As an example,
- suppose we have the <code>smart_ptr</code> smart pointer for which
- we have the traits specialization implemented in the
- <code>smart-ptr-traits.hxx</code> file. Then, we can create an ODB
- compiler options file for this pointer and save it to
- <code>smart-ptr.options</code>:</p>
-
- <pre>
-# Options file for smart_ptr.
-#
---odb-epilogue '#include "smart-ptr-traits.hxx"'
---hxx-prologue '#include "smart-ptr-traits.hxx"'
- </pre>
-
- <p>Now, whenever we compile a header file that uses <code>smart_ptr</code>,
- we can specify the following command line option to make sure it is
- recognized by the ODB compiler as a smart pointer and the traits file
- is included in the generated code:</p>
-
- <pre>
---options-file smart-ptr.options
- </pre>
-
- <p>It is also possible to implement a lazy counterpart for your
- smart pointer. The ODB runtime library provides a class template
- that encapsulates the object id management and loading
- functionality that is needed to implement a lazy pointer. All
- you need to do is wrap it with an interface that mimics
- your smart pointer. Using one of the existing lazy pointer
- implementations (either from the ODB runtime library or one
- of the profile libraries) as a base for your implementation
- is the easiest way to get started.</p>
-
-
- <!-- CHAPTER -->
-
- <hr class="page-break"/>
- <h1><a name="7">7 Value Types</a></h1>
-
- <p>In <a href="#3.1">Section 3.1, "Concepts and Terminology"</a> we have
- already discussed the notion of values and value types as well as the
- distinction between simple and composite values. This chapter covers
- simple and composite value types in more detail.</p>
-
- <h2><a name="7.1">7.1 Simple Value Types</a></h2>
-
- <p>A simple value type is a fundamental C++ type or a class type that
- is mapped to a single database column. For each supported database
- system the ODB compiler provides a default mapping to suitable
- database types for most fundamental C++ types, such as <code>int</code>
- or <code>float</code> as well as some class types, such as
- <code>std::string</code>. For more information about the default
- mapping for each database system refer to <a href="#II">Part II,
- Database Systems</a>. We can also provide a custom mapping for
- these or our own value types using the <code>db&nbsp;type</code>
- pragma (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>).</p>
-
- <h2><a name="7.2">7.2 Composite Value Types</a></h2>
-
- <p>A composite value type is a <code>class</code> or <code>struct</code>
- type that is mapped to more than one database column. To declare
- a composite value type we use the <code>db&nbsp;value</code> pragma,
- for example:</p>
-
- <pre class="cxx">
-#pragma db value
-class basic_name
-{
- ...
-
- std::string first_;
- std::string last_;
-};
- </pre>
-
- <p>The complete version of the above code fragment and the other code
- samples presented in this section can be found in the <code>composite</code>
- example in the <code>odb-examples</code> package.</p>
-
- <p>A composite value type does not have to define a default constructor,
- unless it is used as an element of a container. In this case the
- default constructor can be made private provided we also make the
- <code>odb::access</code> class, defined in the
- <code>&lt;odb/core.hxx></code> header, a friend of this value type.
- For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/core.hxx>
-
-#pragma db value
-class basic_name
-{
-public:
- basic_name (const std::string&amp; first, const std::string&amp; last);
-
- ...
-
-private:
- friend class odb::access;
-
- basic_name () {} // Needed for storing basic_name in containers.
-
- ...
-};
- </pre>
-
- <p>The ODB compiler also needs access to the non-transient
- (<a href="#14.4.11">Section 14.4.11, "<code>transient</code>"</a>)
- data members of a composite value type. It uses the same mechanisms
- as for persistent classes which are discussed in
- <a href="#3.2">Section 3.2, "Declaring Persistent Objects and
- Values"</a>.</p>
-
- <p>The members of a composite value can be other value types (either
- simple or composite), containers (<a href="#5">Chapter 5,
- "Containers"</a>), and pointers to objects (<a href="#6">Chapter 6,
- "Relationships"</a>).
- Similarly, a composite value type can be used in object members,
- as an element of a container, and as a base for another composite
- value type. In particular, composite value types can be used as
- element types in set containers (<a href="#5.2">Section 5.2, "Set
- and Multiset Containers"</a>) and as key types in map containers
- (<a href="#5.3">Section 5.3, "Map and Multimap Containers"</a>).
- A composite value type that is used as an element of a container
- cannot contain other containers since containers of containers
- are not allowed. The following example illustrates some of the
- possible use cases:</p>
-
- <pre class="cxx">
-#pragma db value
-class basic_name
-{
- ...
-
- std::string first_;
- std::string last_;
-};
-
-typedef std::vector&lt;basic_name> basic_names;
-
-#pragma db value
-class name_extras
-{
- ...
-
- std::string nickname_;
- basic_names aliases_;
-};
-
-#pragma db value
-class name: public basic_name
-{
- ...
-
- std::string title_;
- name_extras extras_;
-};
-
-#pragma db object
-class person
-{
- ...
-
- name name_;
-};
- </pre>
-
- <p>A composite value type can be defined inside a persistent class,
- view, or another composite value and even made private, provided
- we make <code>odb::access</code> a friend of the containing class,
- for example:</p>
-
-<pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db value
- class name
- {
- ...
-
- std::string first_;
- std::string last_;
- };
-
- name name_;
-};
- </pre>
-
- <p>A composite value type can also be defined as an instantiation
- of a C++ class template, for example:</p>
-
- <pre class="cxx">
-template &lt;typename T>
-struct point
-{
- T x;
- T y;
- T z;
-};
-
-typedef point&lt;int> int_point;
-#pragma db value(int_point)
-
-#pragma db object
-class object
-{
- ...
-
- int_point center_;
-};
- </pre>
-
- <p>Note that the database support code for such a composite value type
- is generated when compiling the header containing the
- <code>db&nbsp;value</code> pragma and not the header containing
- the template definition or the <code>typedef</code> name. This
- allows us to use templates defined in other files, such as
- <code>std::pair</code> defined in the <code>utility</code>
- standard header file:</p>
-
- <pre class="cxx">
-#include &lt;utility> // std::pair
-
-typedef std::pair&lt;std::string, std::string> phone_numbers;
-#pragma db value(phone_numbers)
-
-#pragma db object
-class person
-{
- ...
-
- phone_numbers phone_;
-};
- </pre>
-
- <p>We can also use data members from composite value types
- in database queries (<a href="#4">Chapter 4, "Querying the
- Database"</a>). For each composite value in a persistent class, the
- query class defines a nested member that contains members corresponding
- to the data members in the value type. We can then use the member access
- syntax (.) to refer to data members in value types. For example, the
- query class for the <code>person</code> object presented above
- contains the <code>name</code> member (its name is derived from
- the <code>name_</code> data member) which in turn contains the
- <code>extras</code> member (its name is derived from the
- <code>name::extras_</code> data member of the composite value type).
- This process continues recursively for nested composite value types
- and, as a result, we can use the <code>query::name.extras.nickname</code>
- expression while querying the database for the <code>person</code>
- objects. For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;person> query;
-typedef odb::result&lt;person> result;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;person> (
- query::name.extras.nickname == "Squeaky"));
-
-...
-
-t.commit ();
- </pre>
-
- <h3><a name="7.2.1">7.2.1 Composite Object Ids</a></h3>
-
- <p>An object id can be of a composite value type, for example:</p>
-
- <pre class="cxx">
-#pragma db value
-class name
-{
- ...
-
- std::string first_;
- std::string last_;
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id
- name name_;
-};
- </pre>
-
- <p>However, a value type that can be used as an object id has a number
- of restrictions. Such a value type cannot have container, object
- pointer, or read-only data members. It also must be
- default-constructible, copy-constructible, and copy-assignable.
- Furthermore, if the persistent class in which
- this composite value type is used as object id has session support
- enabled (<a href="#11">Chapter 11, "Session"</a>), then it must also
- implement the less-than comparison operator (<code>operator&lt;</code>).</p>
-
- <h3><a name="7.2.2">7.2.2 Composite Value Column and Table Names</a></h3>
-
- <p>Customizing a column name for a data member of a simple value
- type is straightforward: we simply specify the desired name with
- the <code>db&nbsp;column</code> pragma (<a href="#14.4.9">Section
- 14.4.9, "<code>column</code>"</a>). For composite value
- types things are slightly more complex since they are mapped to
- multiple columns. Consider the following example:</p>
-
- <pre class="cxx">
-#pragma db value
-class name
-{
- ...
-
- std::string first_;
- std::string last_;
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-
- name name_;
-};
- </pre>
-
- <p>The column names for the <code>first_</code> and <code>last_</code>
- members are constructed by using the sanitized name of the
- <code>person::name_</code> member as a prefix and the names of the
- members in the value type (<code>first_</code> and <code>last_</code>)
- as suffixes. As a result, the database schema for the above classes
- will look like this:</p>
-
- <pre class="sql">
-CREATE TABLE person (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- name_first TEXT NOT NULL,
- name_last TEXT NOT NULL);
- </pre>
-
- <p>We can customize both the prefix and the suffix using the
- <code>db&nbsp;column</code> pragma as shown in the following
- example:</p>
-
- <pre class="cxx">
-#pragma db value
-class name
-{
- ...
-
- #pragma db column("first_name")
- std::string first_;
-
- #pragma db column("last_name")
- std::string last_;
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db column("person_")
- name name_;
-};
- </pre>
-
- <p>The database schema changes as follows:</p>
-
- <pre class="sql">
-CREATE TABLE person (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- person_first_name TEXT NOT NULL,
- person_last_name TEXT NOT NULL);
- </pre>
-
- <p>We can also make the column prefix empty, for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db column("")
- name name_;
-};
- </pre>
-
- <p>This will result in the following schema:</p>
-
- <pre class="sql">
-CREATE TABLE person (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- first_name TEXT NOT NULL,
- last_name TEXT NOT NULL);
- </pre>
-
- <p>The same principle applies when a composite value type is used
- as an element of a container, except that instead of
- <code>db&nbsp;column</code>, either the <code>db&nbsp;value_column</code>
- (<a href="#14.4.36">Section 14.4.36, "<code>value_column</code>"</a>) or
- <code>db&nbsp;key_column</code>
- (<a href="#14.4.35">Section 14.4.35, "<code>key_column</code>"</a>)
- pragmas are used to specify the column prefix.</p>
-
- <p>When a composite value type contains a container, an extra table
- is used to store its elements (<a href="#5">Chapter 5, "Containers"</a>).
- The names of such tables are constructed in a way similar to the
- column names, except that by default both the object name and the
- member name are used as a prefix. For example:</p>
-
- <pre class="cxx">
-#pragma db value
-class name
-{
- ...
-
- std::string first_;
- std::string last_;
- std::vector&lt;std::string> nicknames_;
-};
-
-#pragma db object
-class person
-{
- ...
-
- name name_;
-};
- </pre>
-
- <p>The corresponding database schema will look like this:</p>
-
- <pre class="sql">
-CREATE TABLE person_name_nicknames (
- object_id BIGINT UNSIGNED NOT NULL,
- index BIGINT UNSIGNED NOT NULL,
- value TEXT NOT NULL)
-
-CREATE TABLE person (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- name_first TEXT NOT NULL,
- name_last TEXT NOT NULL);
- </pre>
-
- <p>To customize the container table name we can use the
- <code>db&nbsp;table</code> pragma (<a href="#14.4.20">Section
- 14.4.20, "<code>table</code>"</a>), for example:</p>
-
- <pre class="cxx">
-#pragma db value
-class name
-{
- ...
-
- #pragma db table("nickname")
- std::vector&lt;std::string> nicknames_;
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db table("person_")
- name name_;
-};
- </pre>
-
- <p>This will result in the following schema changes:</p>
-
- <pre class="sql">
-CREATE TABLE person_nickname (
- object_id BIGINT UNSIGNED NOT NULL,
- index BIGINT UNSIGNED NOT NULL,
- value TEXT NOT NULL)
- </pre>
-
- <p>Similar to columns, we can make the table prefix empty.</p>
-
-
- <h2><a name="7.3">7.3 Pointers and <code>NULL</code> Value Semantics</a></h2>
-
- <p>Relational database systems have a notion of the special
- <code>NULL</code> value that is used to indicate the absence
- of a valid value in a column. While by default ODB maps
- values to columns that do not allow <code>NULL</code> values,
- it is possible to change that with the <code>db&nbsp;null</code>
- pragma (<a href="#14.4.6">Section 14.4.6,
- "<code>null</code>/<code>not_null</code>"</a>).</p>
-
- <p>To properly support the <code>NULL</code> semantics, the
- C++ value type must have a notion of a <code>NULL</code>
- value or a similar special state concept. Most basic
- C++ types, such as <code>int</code> or <code>std::string</code>,
- do not have this notion and therefore cannot be used directly
- for <code>NULL</code>-enabled data members (in the case of a
- <code>NULL</code> value being loaded from the database,
- such data members will be default-initialized).</p>
-
- <p>To allow the easy conversion of value types that do not support
- the <code>NULL</code> semantics into the ones that do, ODB
- provides the <code>odb::nullable</code> class template. It
- allows us to wrap an existing C++ type into a container-like
- class that can either be <code>NULL</code> or contain a
- value of the wrapped type. ODB also automatically enables
- the <code>NULL</code> values for data members of the
- <code>odb::nullable</code> type. For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/nullable.hxx>
-
-#pragma db object
-class person
-{
- ...
-
- std::string first_; // TEXT NOT NULL
- odb::nullable&lt;std::string> middle_; // TEXT NULL
- std::string last_; // TEXT NOT NULL
-};
- </pre>
-
- <p>The <code>odb::nullable</code> class template is defined
- in the <code>&lt;odb/nullable.hxx></code> header file and
- has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- template &lt;typename T>
- class nullable
- {
- public:
- typedef T value_type;
-
- nullable ();
- nullable (const T&amp;);
- nullable (const nullable&amp;);
- template &lt;typename Y> explicit nullable (const nullable&lt;Y>&amp;);
-
- nullable&amp; operator= (const T&amp;);
- nullable&amp; operator= (const nullable&amp;);
- template &lt;typename Y> nullable&amp; operator= (const nullable&lt;Y>&amp;);
-
- void swap (nullable&amp;);
-
- // Accessor interface.
- //
- bool null () const;
-
- T&amp; get ();
- const T&amp; get () const;
-
- // Pointer interface.
- //
- operator bool_convertible () const;
-
- T* operator-> ();
- const T* operator-> () const;
-
- T&amp; operator* ();
- const T&amp; operator* () const;
-
- // Reset to the NULL state.
- //
- void reset ();
- };
-}
- </pre>
-
- <p>The following example shows how we can use this interface:</p>
-
- <pre class="cxx">
- nullable&lt;string> ns;
-
- // Using the accessor interface.
- //
- if (ns.null ())
- {
- s = "abc";
- }
- else
- {
- string s (ns.get ());
- ns.reset ();
- }
-
- // The same using the pointer interface.
- //
- if (!ns)
- {
- s = "abc";
- }
- else
- {
- string s (*ns);
- ns.reset ();
- }
- </pre>
-
-
- <p>The <code>odb::nullable</code> class template requires the wrapped
- type to have public default and copy constructors as well as the
- copy assignment operator. Note also that the <code>odb::nullable</code>
- implementation is not the most efficient in that it always contains
- a fully constructed value of the wrapped type. This is normally
- not a concern for simple types such as the C++ fundamental
- types or <code>std::string</code>. However, it may become
- an issue for more complex types. In such cases you may want to
- consider using a more efficient implementation of the
- <em>optional value</em> concept such as the
- <code>optional</code> class template from Boost
- (<a href="#23.4">Section 23.4, "Optional Library"</a>).</p>
-
- <p>Another common C++ representation of a value that can be
- <code>NULL</code> is a pointer. ODB will automatically
- handle data members that are pointers to values, however,
- it will not automatically enable <code>NULL</code> values
- for such data members, as is the case for <code>odb::nullable</code>.
- Instead, if the <code>NULL</code> value is desired, we will
- need to enable it explicitly using the <code>db&nbsp;null</code>
- pragma. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- std::string first_;
-
- #pragma db null
- std::auto_ptr&lt;std::string> middle_;
-
- std::string last_;
-};
- </pre>
-
- <p>The ODB compiler includes built-in support for using
- <code>std::auto_ptr</code>, <code>std::unique_ptr</code> (C++11),
- and <code>shared_ptr</code> (TR1 or C++11) as pointers to values.
- Plus, ODB profile libraries, that are
- available for commonly used frameworks and libraries (such as Boost and
- Qt), provide support for smart pointers found in these frameworks
- and libraries (<a href="#III">Part III, "Profiles"</a>).</p>
-
- <p>ODB also supports the <code>NULL</code> semantics for composite
- values. In the relational database the <code>NULL</code> composite
- value is translated to <code>NULL</code> values for all the simple
- data members of this composite value. For example:</p>
-
- <pre class="cxx">
-#pragma db value
-struct name
-{
- std::string first_;
- odb::nullable&lt;std::string> middle_;
- std::string last_;
-};
-
-#pragma db object
-class person
-{
- ...
- odb::nullable&lt;name> name_;
-};
- </pre>
-
- <p>ODB does not support the <code>NULL</code> semantics for containers.
- This also means that a composite value that contains a container
- cannot be <code>NULL</code>. With this limitation in mind, we can
- still use smart pointers in data members of container types. The
- only restriction is that these pointers must not be <code>NULL</code>.
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- std::auto_ptr&lt;std::vector&lt;std::string> > aliases_;
-};
- </pre>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="8">8 Inheritance</a></h1>
-
- <p>In C++ inheritance can be used to achieve two different goals.
- We can employ inheritance to reuse common data and functionality
- in multiple classes. For example:</p>
-
- <pre class="cxx">
-class person
-{
-public:
- const std::string&amp; first () const;
- const std::string&amp; last () const;
-
-private:
- std::string first_;
- std::string last_;
-};
-
-class employee: public person
-{
- ...
-};
-
-class contractor: public person
-{
- ...
-};
- </pre>
-
- <p>In the above example both the <code>employee</code> and
- <code>contractor</code> classes inherit the <code>first_</code>
- and <code>last_</code> data members as well as the <code>first()</code>
- and <code>last()</code> accessors from the <code>person</code> base
- class.</p>
-
- <p>A common trait of this inheritance style, referred to as <em>reuse
- inheritance</em> from now on, is the lack of virtual functions and
- a virtual destructor in the base class. Also with this style the
- application code is normally written in terms of the derived classes
- instead of the base.</p>
-
- <p>The second way to utilize inheritance in C++ is to provide polymorphic
- behavior through a common interface. In this case the base class
- defines a number of virtual functions and, normally, a virtual
- destructor while the derived classes provide specific
- implementations of these virtual functions. For example:</p>
-
- <pre class="cxx">
-class person
-{
-public:
- enum employment_status
- {
- unemployed,
- temporary,
- permanent,
- self_employed
- };
-
- virtual employment_status
- employment () const = 0;
-
- virtual
- ~person ();
-};
-
-class employee: public person
-{
-public:
- virtual employment_status
- employment () const
- {
- return temporary_ ? temporary : permanent;
- }
-
-private:
- bool temporary_;
-};
-
-class contractor: public person
-{
-public:
- virtual employment_status
- employment () const
- {
- return self_employed;
- }
-};
- </pre>
-
- <p>With this inheritance style, which we will call <em>polymorphism
- inheritance</em>, the application code normally works with derived
- classes via the base class interface. Note also that it is very common
- to mix both styles in the same hierarchy. For example, the above two
- code fragments can be combined so that the <code>person</code> base
- class provides the common data members and functions as well as
- defines the polymorphic interface.</p>
-
- <p>The following sections describe the available strategies for
- mapping reuse and polymorphism inheritance styles to a relational
- data model. Note also that the distinction between the two styles is
- conceptual rather than formal. For example, it is possible to treat
- a class hierarchy that defines virtual functions as a case of reuse
- inheritance if this results in the desired database mapping and
- semantics.</p>
-
- <p>Generally, classes that employ reuse inheritance are mapped to
- completely independent entities in the database. They use different
- object id spaces and should always be passed to and returned from
- the database operations as pointers or references to derived types.
- In other words, from the persistence point of view, such classes
- behave as if the data members from the base classes were copied
- verbatim into the derived ones.</p>
-
- <p>In contrast, classes that employ polymorphism inheritance share
- the object id space and can be passed to and returned from the
- database operations <em>polymorphically</em> as pointers or
- references to the base class.</p>
-
- <p>For both inheritance styles it is sometimes desirable to prevent
- instances of a base class from being stored in the database.
- To achieve this a persistent
- class can be declared abstract using the <code>db&nbsp;abstract</code>
- pragma (<a href="#14.1.3">Section 14.1.3, "<code>abstract</code>"</a>).
- Note that a <em>C++-abstract</em> class, or a class that
- has one or more pure virtual functions and therefore cannot be
- instantiated, is also <em>database-abstract</em>. However, a
- database-abstract class is not necessarily C++-abstract. The
- ODB compiler automatically treats C++-abstract classes as
- database-abstract.</p>
-
- <h2><a name="8.1">8.1 Reuse Inheritance</a></h2>
-
- <p>Each non-abstract class from the reuse inheritance hierarchy is
- mapped to a separate database table that contains all its data
- members, including those inherited from base classes. An abstract
- persistent class does not have to define an object id, nor a default
- constructor, and it does not have a corresponding database table.
- An abstract class cannot be a pointed-to object in a relationship.
- Multiple inheritance is supported as long as each base
- class is only inherited once. The following example shows a
- persistent class hierarchy employing reuse inheritance:</p>
-
- <pre class="cxx">
-// Abstract person class. Note that it does not declare the
-// object id.
-//
-#pragma db object abstract
-class person
-{
- ...
-
- std::string first_;
- std::string last_;
-};
-
-// Abstract employee class. It derives from the person class and
-// declares the object id for all the concrete employee types.
-//
-#pragma db object abstract
-class employee: public person
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-};
-
-// Concrete permanent_employee class. Note that it doesn't define
-// any data members of its own.
-//
-#pragma db object
-class permanent_employee: public employee
-{
- ...
-};
-
-// Concrete temporary_employee class. It adds the employment
-// duration in months.
-//
-#pragma db object
-class temporary_employee: public employee
-{
- ...
-
- unsigned long duration_;
-};
-
-// Concrete contractor class. It derives from the person class
-// (and not employee; an independent contractor is not considered
-// an employee). We use the contractor's external email address
-// as the object id.
-//
-#pragma db object
-class contractor: public person
-{
- ...
-
- #pragma db id
- std::string email_;
-};
- </pre>
-
- <p>The sample database schema for this hierarchy is shown below.</p>
-
- <pre class="sql">
-CREATE TABLE permanent_employee (
- first TEXT NOT NULL,
- last TEXT NOT NULL,
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT);
-
-CREATE TABLE temporary_employee (
- first TEXT NOT NULL,
- last TEXT NOT NULL,
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
- duration BIGINT UNSIGNED NOT NULL);
-
-CREATE TABLE contractor (
- first TEXT NOT NULL,
- last TEXT NOT NULL,
- email VARCHAR (128) NOT NULL PRIMARY KEY);
- </pre>
-
- <p>The complete version of the code presented in this section is
- available in the <code>inheritance/reuse</code> example in the
- <code>odb-examples</code> package.</p>
-
- <h2><a name="8.2">8.2 Polymorphism Inheritance</a></h2>
-
- <p>There are three general approaches to mapping a polymorphic
- class hierarchy to a relational database. These are
- <em>table-per-hierarchy</em>, <em>table-per-difference</em>,
- and <em>table-per-class</em>. With the table-per-hierarchy
- mapping, all the classes in a hierarchy are stored in a single,
- "wide" table. <code>NULL</code> values are stored in columns
- corresponding to data members of derived classes that are
- not present in any particular instance.</p>
-
- <p>In the table-per-difference mapping, each class is mapped
- to a separate table. For a derived class, this table contains
- only columns corresponding to the data members added by this
- derived class.</p>
-
- <p>Finally, in the table-per-class mapping, each class is mapped
- to a separate table. For a derived class, this table contains
- columns corresponding to all the data members, from this derived
- class all the way down to the root of the hierarchy.</p>
-
- <p>The table-per-difference mapping is generally considered as
- having the best balance of flexibility, performance, and space
- efficiency. It also results in a more canonical relational
- database model compared to the other two approaches. As a
- result, this is the mapping currently implemented in ODB.
- Other mappings may be supported in the future. Note that
- multiple polymorphism inheritance or mixing polymorphism and
- reuse inheritance is not supported.</p>
-
- <p>A pointer or reference to an ordinary, non-polymorphic object
- has just one type &mdash; the class type of that object. When we
- start working with polymorphic objects, there are two types
- to consider: the <em>static type</em>, or the declaration type
- of a reference or pointer, and the object's actual or <em>dynamic
- type</em>. An example will help illustrate the difference:</p>
-
- <pre class="cxx">
-class person {...};
-class employee: public person {...};
-
-person p;
-employee e;
-
-person&amp; r1 (p);
-person&amp; r2 (e);
-
-auto_ptr&lt;person> p1 (new employee);
- </pre>
-
- <p>In the above example, the <code>r1</code> reference's both static
- and dynamic types are <code>person</code>.
- In contrast, the <code>r2</code> reference's static type is
- <code>person</code> while its dynamic type (the actual object
- that it refers to) is <code>employee</code>. Similarly,
- <code>p1</code> points to the object of the <code>person</code>
- static type but <code>employee</code> dynamic type.</p>
-
- <p>In C++, the primary mechanisms for working with polymorphic objects
- are virtual functions. We call a virtual function only knowing the
- object's static type, but the version corresponding to the object's
- dynamic type is automatically executed. This is the essence of
- runtime polymorphism support in C++: we can operate in terms of a base
- class interface but get the derived class' behavior. Similarly, the
- essence of the runtime polymorphism support in ODB is to allow us to
- persist, load, update, and query in terms of the base class interface
- but have the derived class actually stored in the database.</p>
-
- <p>To declare a persistent class as polymorphic we use the
- <code>db&nbsp;polymorphic</code> pragma. We only need to
- declare the root class of a hierarchy as polymorphic; ODB will
- treat all the derived classes as polymorphic automatically. For
- example:</p>
-
- <pre class="cxx">
-#pragma db object polymorphic
-class person
-{
- ...
-
- virtual
- ~person () = 0; // Automatically abstract.
-
- #pragma db id auto
- unsigned long id_;
-
- std::string first_;
- std::string last_;
-};
-
-#pragma db object
-class employee: public person
-{
- ...
-
- bool temporary_;
-};
-
-#pragma db object
-class contractor: public person
-{
-
- std::string email_;
-};
- </pre>
-
- <p>A persistent class hierarchy declared polymorphic must also be
- polymorphic in the C++ sense, that is, the root class must
- declare or inherit at least one virtual function. It is
- recommended that the root class also declares a virtual destructor.
- The root class of the polymorphic hierarchy must contain
- the data member designated as object id (a persistent class
- without an object id cannot be polymorphic). Note also that,
- unlike reuse inheritance, abstract polymorphic classes have
- a table in the database, just like non-abstract classes.</p>
-
- <p>Persistent classes in the same polymorphic hierarchy must use the
- same kind of object pointer (<a href="#3.3">Section 3.3,
- "Object and View Pointers"</a>). If the object pointer
- for the root class is specified as a template or using the
- special raw pointer syntax (<code>*</code>), then the ODB
- compiler will automatically use the same object pointer
- for all the derived classes. For example:</p>
-
- <pre class="cxx">
-#pragma db object polymorphic pointer(std::shared_ptr)
-class person
-{
- ...
-};
-
-#pragma db object // Object pointer is std::shared_ptr&lt;employee>.
-class employee: public person
-{
- ...
-};
-
-#pragma db object // Object pointer is std::shared_ptr&lt;contractor>.
-class contractor: public person
-{
- ...
-};
- </pre>
-
- <p>Similarly, if we enable or disable session support
- (<a href="#11">Chapter 11, "Session"</a>) for the root class, then
- the ODB compiler will automatically enable or disable it for all
- the derived classes.</p>
-
- <p>For polymorphic persistent classes, all the database operations can
- be performed on objects with different static and dynamic types.
- Similarly, operations that load persistent objects from the
- database (<code>load()</code>, <code>query()</code>, etc.), can
- return objects with different static and dynamic types. For
- example:</p>
-
- <pre class="cxx">
-unsigned long id1, id2;
-
-// Persist.
-//
-{
- shared_ptr&lt;person> p1 (new employee (...));
- shared_ptr&lt;person> p2 (new contractor (...));
-
- transaction t (db.begin ());
- id1 = db.persist (p1); // Stores employee.
- id2 = db.persist (p2); // Stores contractor.
- t.commit ();
-}
-
-// Load.
-//
-{
- shared_ptr&lt;person> p;
-
- transaction t (db.begin ());
- p = db.load&lt;person> (id1); // Loads employee.
- p = db.load&lt;person> (id2); // Loads contractor.
- t.commit ();
-}
-
-// Query.
-//
-{
- typedef odb::query&lt;person> query;
- typedef odb::result&lt;person> result;
-
- transaction t (db.begin ());
-
- result r (db.query&lt;person> (query::last == "Doe"));
-
- for (result::iterator i (r.begin ()); i != r.end (); ++i)
- {
- person&amp; p (*i); // Can be employee or contractor.
- }
-
- t.commit ();
-}
-
-// Update.
-//
-{
- shared_ptr&lt;person> p;
- shared_ptr&lt;employee> e;
-
- transaction t (db.begin ());
-
- e = db.load&lt;employee> (id1);
- e->temporary (false);
- p = e;
- db.update (p); // Updates employee.
-
- t.commit ();
-}
-
-// Erase.
-//
-{
- shared_ptr&lt;person> p;
-
- transaction t (db.begin ());
- p = db.load&lt;person> (id1); // Loads employee.
- db.erase (p); // Erases employee.
- db.erase&lt;person> (id2); // Erases contractor.
- t.commit ();
-}
- </pre>
-
-
- <p>The table-per-difference mapping, as supported by ODB, requires
- two extra columns, in addition to those corresponding to the
- data members. The first, called <em>discriminator</em>, is added
- to the table corresponding to the root class of the hierarchy.
- This column is used to determine the dynamic type of each
- object. The second column is added to tables corresponding
- to the derived classes and contains the object id. This
- column is used to form a foreign key constraint referencing
- the root class table.</p>
-
- <p>When querying the database for polymorphic objects, it is
- possible to obtain the discriminator value without
- instantiating the object. For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;person> query;
-typedef odb::result&lt;person> result;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;person> (query::last == "Doe"));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
-{
- std::string d (i.discriminator ());
- ...
-}
-
-t.commit ();
- </pre>
-
- <p>In the current implementation, ODB has limited support for
- customizing names, types, and values of the extra columns.
- Currently, the discriminator column is always called
- <code>typeid</code> and contains a namespace-qualified class
- name (for example, <code>"employee"</code> or
- <code>"hr::employee"</code>). The id column in the derived
- class table has the same name as the object id column in
- the root class table. Future versions of ODB will add support
- for customizing these extra columns.</p>
-
- <p>The sample database schema for the above polymorphic hierarchy
- is shown below.</p>
-
- <pre class="sql">
-CREATE TABLE person (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
- typeid VARCHAR(128) NOT NULL,
- first TEXT NOT NULL,
- last TEXT NOT NULL);
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- temporary TINYINT(1) NOT NULL,
-
- CONSTRAINT employee_id_fk
- FOREIGN KEY (id)
- REFERENCES person (id)
- ON DELETE CASCADE);
-
-CREATE TABLE contractor (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- email TEXT NOT NULL,
-
- CONSTRAINT contractor_id_fk
- FOREIGN KEY (id)
- REFERENCES person (id)
- ON DELETE CASCADE);
- </pre>
-
- <p>The complete version of the code presented in this section is
- available in the <code>inheritance/polymorphism</code> example
- in the <code>odb-examples</code> package.</p>
-
- <h3><a name="8.2.1">8.2.1 Performance and Limitations</a></h3>
-
- <p>A database operation on a non-polymorphic object normally translates
- to a single database statement execution (objects with containers
- and eager object pointers can be the exception). Because polymorphic
- objects have their data members
- stored in multiple tables, some database operations on such objects
- may result in multiple database statements being executed while others
- may require more complex statements. There is also some functionality
- that is not available to polymorphic objects.</p>
-
- <p>The first part of this section discusses the performance implications
- to keep in mind when designing and working with polymorphic hierarchies.
- The second part talks about limitations of polymorphic objects.</p>
-
- <p>The most important aspect of a polymorphic hierarchy that
- affects database performance is its depth. The distance between
- the root of the hierarchy and the derived class translates
- directly to the number of database statements that will have to
- be executed in order to persist, update, or erase this derived class.
- It also translates directly to the number of SQL <code>JOIN</code>
- clauses that will be needed to load or query the database for this
- derived class. As a result, to achieve best performance, we should
- try to keep our polymorphic hierarchies as flat as possible.</p>
-
- <p>When loading an object or querying the database for objects,
- ODB will need to execute two statements if this object's static
- and dynamic types are different but only one statement if
- they are the same. This example will help illustrate the
- difference:</p>
-
- <pre class="cxx">
-unsigned long id;
-
-{
- employee e (...);
-
- transaction t (db.begin ());
- id = db.persist (e);
- t.commit ();
-}
-
-{
- shared_ptr&lt;person> p;
-
- transaction t (db.begin ());
- p = db.load&lt;person> (id); // Requires two statement.
- p = db.load&lt;employee> (id); // Requires only one statement.
- t.commit ();
-}
- </pre>
-
- <p>As a result, we should try to load and query using the most
- derived class possible.</p>
-
- <p>Finally, for polymorphic objects, erasing via the object instance
- is faster than erasing via its object id. In the former case the
- object's dynamic type can be determined locally in the application
- while in the latter case an extra statement has to be executed to
- achieve the same result. For example:</p>
-
- <pre class="cxx">
-shared_ptr&lt;person> p = ...;
-
-transaction t (db.begin ());
-db.erase&lt;person> (p.id ()); // Slower (executes extra statement).
-db.erase (p); // Faster.
-t.commit ();
- </pre>
-
- <p>Polymorphic objects can use all the mechanisms that are available
- to ordinary objects. These include containers (<a href="#5">Chapter 5,
- "Containers"</a>), object relationships, including to polymorphic
- objects (<a href="#6">Chapter 6, "Relationships"</a>), views
- (<a href="#10">Chapter 10, "Views"</a>), session (<a href="#11">Chapter
- 11, "Session"</a>), and optimistic concurrency (<a href="#12">Chapter
- 12, "Optimistic Concurrency"</a>). There are, however, a few
- limitations, mainly due to the underlying use of SQL to access the
- data.</p>
-
- <p>When a polymorphic object is "joined" in a view, and the join
- condition (either in the form of an object pointer or a custom
- condition) comes from the object itself (as opposed to one of
- the objects joined previously), then this condition must only
- use data members from the derived class. For example, consider
- the following polymorphic object hierarchy and a view:</p>
-
-
- <pre class="cxx">
-#pragma db object polymorphic
-class employee
-{
- ...
-};
-
-#pragma db object
-class permanent_employee: public employee
-{
- ...
-};
-
-#pragma db object
-class temporary_employee: public employee
-{
- ...
-
- shared_ptr&lt;permanent_employee> manager_;
-};
-
-#pragma db object
-class contractor: public temporary_employee
-{
- shared_ptr&lt;permanent_employee> manager_;
-};
-
-#pragma db view object(permanent_employee) \
- object(contractor: contractor::manager_)
-struct contractor_manager
-{
- ...
-};
- </pre>
-
- <p>This view will not function correctly because the join condition
- (<code>manager_</code>) comes from the base class
- (<code>temporary_employee</code>) instead of the derived
- (<code>contractor</code>). The reason for this limitation is the
- <code>JOIN</code> clause order in the underlying SQL <code>SELECT</code>
- statement. In the view presented above, the table corresponding
- to the base class (<code>temporary_employee</code>) will have to
- be joined first which will result in this view matching both
- the <code>temporary_employee</code> and <code>contractor</code>
- objects instead of just <code>contractor</code>. It is usually
- possible to resolve this issue by reordering the objects in the
- view. Our example, for instance, can be fixed by swapping the
- two objects:</p>
-
- <pre class="cxx">
-#pragma db view object(contractor) \
- object(permanent_employee: contractor::manager_)
-struct contractor_manager
-{
- ...
-};
- </pre>
-
- <p>The <code>erase_query()</code> database function (<a href="#3.11">Section
- 3.11, "Deleting Persistent Objects"</a>) also has limited functionality
- when used on polymorphic objects. Because many database implementations
- do not support <code>JOIN</code> clauses in the SQL <code>DELETE</code>
- statement, only data members from the derived class being erased can
- be used in the query condition. For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee> query;
-
-transaction t (db.begin ());
-db.erase_query&lt;employee> (query::permanent); // Ok.
-db.erase_query&lt;employee> (query::last == "Doe"); // Error.
-t.commit ();
- </pre>
-
- <h2><a name="8.3">8.3 Mixed Inheritance</a></h2>
-
- <p>It is possible to mix the reuse and polymorphism inheritance
- styles in the same hierarchy. In this case, the reuse inheritance
- must be used for the "bottom" (base) part of the hierarchy while
- the polymorphism inheritance &mdash; for the "top" (derived) part.
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-};
-
-#pragma db object polymorphic
-class employee: public person // Reuse inheritance.
-{
- ...
-};
-
-#pragma db object
-class temporary_employee: public employee // Polymorphism inheritance.
-{
- ...
-};
-
-#pragma db object
-class permanent_employee: public employee // Polymorphism inheritance.
-{
- ...
-};
- </pre>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="9">9 Sections</a></h1>
-
- <p>ODB sections are an optimization mechanism that allows us to
- partition data members of a persistent class into groups that
- can be separately loaded and/or updated. This can be useful,
- for example, if an object contains expensive to load or update
- data members (such as <code>BLOB</code>s or containers) and
- that are accessed or modified infrequently. For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/section.hxx>
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db load(lazy) update(manual)
- odb::section keys_;
-
- #pragma db section(keys_) type("BLOB")
- char public_key_[1024];
-
- #pragma db section(keys_) type("BLOB")
- char private_key_[1024];
-};
-
-transaction t (db.begin ());
-
-auto_ptr&lt;person> p (db.load&lt;person> (...)); // Keys are not loaded.
-
-if (need_keys)
-{
- db.load (*p, p->keys_); // Load keys.
- ...
-}
-
-db.update (*p); // Keys are not updated.
-
-if (update_keys)
-{
- ...
- db.update (*p, p->keys_); // Update keys.
-}
-
-t.commit ();
- </pre>
-
- <p>A complete example that shows how to use sections is available in
- the <code>section</code> directory in the <code>odb-examples</code>
- package.</p>
-
- <p>Why do we need to group data members into sections? Why can't
- each data member be loaded and updated independently if and
- when necessary? The reason for this requirement is that loading
- or updating a group of data members with a single database
- statement is significantly more efficient than loading or updating
- each data member with a separate statement. Because ODB
- prepares and caches statements used to load and update
- persistent objects, generating a custom statement for
- a specific set of data members that need to be loaded or
- updated together is not a viable approach either. To resolve
- this, ODB allows us to group data members that are
- often updated and/or loaded together into sections. To
- achieve the best performance, we should aim to find a balance
- between having too many sections with too few data
- members and too few sections with too many data
- members. We can use the access and modification patterns
- of our application as a base for this decision.</p>
-
- <p>To add a new section to a persistent class we declare a new
- data member of the <code>odb::section</code> type. At this
- point we also need to specify the loading and updating behavior
- of this section with the <code>db&nbsp;load</code> and
- <code>db&nbsp;update</code> pragmas, respectively.</p>
-
- <p>The loading behavior of a section can be either <code>eager</code>
- or <code>lazy</code>. An eager-loaded section is always loaded as
- part of the object load. A lazy-loaded section is not loaded
- as part of the object load and has to be explicitly loaded with
- the <code>database::load()</code> function (discussed below) if
- and when necessary.</p>
-
- <p>The updating behavior of a section can be <code>always</code>,
- <code>change</code>, or <code>manual</code>. An always-updated
- section is always updated as part of the object update,
- provided it has been loaded. A change-updated section
- is only updated as part of the object update if it has been loaded
- and marked as changed. A manually-updated section is never updated
- as part of the object update and has to be explicitly updated with
- the <code>database::update()</code> function (discussed below) if
- and when necessary.</p>
-
- <p>If no loading behavior is specified explicitly, then an eager-loaded
- section is assumed. Similarly, if no updating behavior is specified,
- then an always-updated section is assumed. An eager-loaded, always-updated
- section is pointless and therefore illegal. Only persistent classes
- with an object id can have sections.</p>
-
- <p>To specify that a data member belongs to a section we use the
- <code>db&nbsp;section</code> pragma with the section's member
- name as its single argument. Except for special data members
- such as the object id and optimistic concurrency version, any
- direct, non-transient member of a persistent class can belong
- to a section, including composite values, containers, and
- pointers to objects. For example:</p>
-
- <pre class="cxx">
-#pragma db value
-class text
-{
- std::string data;
- std::string lang;
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db load(lazy)
- odb::section extras_;
-
- #pragma db section(extras_)
- text bio_;
-
- #pragma db section(extras_)
- std::vector&lt;std::string> nicknames_;
-
- #pragma db section(extras_)
- std::shared_ptr&lt;person> emergency_contact_;
-};
- </pre>
-
- <p>An empty section is pointless and therefore illegal, except
- in abstract or polymorphic classes where data members can be
- added to a section by derived classes (see <a href="#9.1">Section
- 9.1, "Sections and Inheritance"</a>).</p>
-
- <p>The <code>odb::section</code> class is defined in the
- <code>&lt;odb/section.hxx></code> header file and has the
- following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- class section
- {
- public:
- // Load state.
- //
- bool
- loaded () const;
-
- void
- unload ();
-
- void
- load ();
-
- // Change state.
- //
- bool
- changed () const;
-
- void
- change ();
-
- // User data.
- //
- unsigned char
- user_data () const;
-
- void
- user_data (unsigned char);
- };
-}
- </pre>
-
- <p>The <code>loaded()</code> accessor can be used to determine whether a
- section is already loaded. The <code>unload()</code> modifier marks a
- loaded section as not loaded. This, for example, can be useful if you
- don't want the section to be reloaded during the object reload. The
- <code>load()</code> modifier marks an unloaded section as loaded
- without actually loading any of its data members. This, for example,
- can be useful if you don't want to load the old state before overwriting
- it with <code>update()</code>.</p>
-
- <p>The <code>changed()</code> accessor can be used to query the
- section's change state. The <code>change()</code> modifier
- marks the section as changed. It is valid to call this modifier
- for an unloaded (or transient) section, however, the state will
- be reset back to unchanged once the section (or object) is loaded.
- The change state is only relevant to sections with change-updated
- behavior and is ignored for all other sections.</p>
-
- <p>The size of the section class is one byte with four bits available
- to store a custom state via the <code>user_data()</code> accessor
- and modifier.</p>
-
- <p>The <code>odb::database</code> class provides special
- versions of the <code>load()</code> and <code>update()</code>
- functions that allow us to load and update sections of a
- persistent class. Their signatures are as follows:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- void
- load (T&amp; object, section&amp;);
-
- template &lt;typename T>
- void
- update (const T&amp; object, const section&amp;);
- </pre>
-
- <p>Before calling the section <code>load()</code> function, the
- object itself must already be loaded. If the section is already
- loaded, then the call to <code>load()</code> will reload its
- data members. It is illegal to explicitly load an eager-loaded
- section.</p>
-
- <p>Before calling the section <code>update()</code> function, the
- section (and therefore the object) must be in the loaded state.
- If the section is not loaded, the <code>odb::section_not_loaded</code>
- exception is thrown. The section <code>update()</code> function
- does not check but does clear the section's change state. In
- other words, section <code>update()</code> will always update
- section data members in the database and clear the change flag.
- Note also that any section, that is, always-, change-, or
- manually-updated, can be explicitly updated with this function.</p>
-
- <p>Both section <code>load()</code> and <code>update()</code>, just
- like the rest of the database operations, must be performed within
- a transaction. Notice also that both <code>load()</code> and
- <code>update()</code> expect a reference to the section as
- their second argument. This reference must refer to the data
- member in the object passed as the first argument. If instead
- it refers to some other instance of the <code>section</code>
- class, for example, a local copy or a temporary, then the
- <code>odb::section_not_in_object</code> exception is thrown.
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
-public:
- ...
-
- odb::section
- keys () const {return keys_;}
-
-private:
- odb::section keys_;
-
- ...
-};
-
-auto_ptr&lt;person> p (db.load&lt;person> (...));
-
-section s (p->keys ());
-db.load (*p, s); // Throw section_not_in_object, copy.
-
-db.update (*p, p->keys ()); // Throw section_not_in_object, copy.
- </pre>
-
- <p>At first glance it may seem more appropriate to make the
- <code>section</code> class non-copyable in order to prevent
- such errors from happening. However, it is perfectly reasonable
- to expect to be able to copy (or assign) sections as part of
- the object copying (or assignment). As a result, sections are
- left copyable and copy-assignable, however, this functionality
- should not be used in accessors or modifiers. Instead, section
- accessors and modifiers should always be by-reference. Here is
- how we can fix our previous example:</p>
-
-<pre class="cxx">
-#pragma db object
-class person
-{
-public:
- ...
-
- const odb::section&amp;
- keys () const {return keys_;}
-
- odb::section&amp;
- keys () {return keys_;}
-
-private:
- odb::section keys_;
-
- ...
-};
-
-auto_ptr&lt;person> p (db.load&lt;person> (...));
-
-section&amp; s (p->keys ());
-db.load (*p, s); // Ok, reference.
-
-db.update (*p, p->keys ()); // Ok, reference.
- </pre>
-
- <p>Several other database operations affect sections. The state of
- a section in a transient object is undefined. That is, before
- the call to object <code>persist()</code> or <code>load()</code>
- functions, or after the call to object <code>erase()</code>
- function, the values returned by the <code>section::loaded()</code> and
- <code>section::changed()</code> accessors are undefined.</p>
-
- <p>After the call to <code>persist()</code>, all sections, including
- eager-loaded ones, are marked as loaded and unchanged. If instead we
- are loading an object with the <code>load()</code> call or as
- a result of a query, then eager-loaded sections are loaded
- and marked as loaded and unchanged while lazy-loaded ones are marked
- as unloaded. If a lazy-loaded section is later loaded with the
- section <code>load()</code> call, then it is marked as loaded and
- unchanged.</p>
-
- <p>When we update an object with the <code>update()</code> call,
- manually-updated sections are ignored while always-updated
- sections are updated if they are loaded. Change-updated
- sections are only updated if they are both loaded and marked
- as changed. After the update, such sections are reset to the
- unchanged state. When we reload an object with the
- <code>reload()</code> call, sections that were loaded are
- automatically reloaded and reset to the unchanged state.</p>
-
- <p>To further illustrate the state transitions of a section,
- consider this example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db load(lazy) update(change)
- odb::section keys_;
-
- ...
-};
-
-transaction t (db.begin ());
-
-person p ("John", "Doe"); // Section state is undefined (transient).
-
-db.persist (p); // Section state: loaded, unchanged.
-
-auto_ptr&lt;person> l (
- db.load&lt;person> (...)); // Section state: unloaded, unchanged.
-
-db.update (*l); // Section not updated since not loaded.
-db.update (p); // Section not updated since not changed.
-
-p.keys_.change (); // Section state: loaded, changed.
-db.update (p); // Section updated, state: loaded, unchanged.
-
-db.update (*l, l->keys_); // Throw section_not_loaded.
-db.update (p, p.keys_); // Section updated even though not changed.
-
-db.reload (*l); // Section not reloaded since not loaded.
-db.reload (p); // Section reloaded, state: loaded, unchanged.
-
-db.load (*l, l->keys_); // Section loaded, state: loaded, unchanged.
-db.load (p, p.keys_); // Section reloaded, state: loaded, unchanged.
-
-db.erase (p); // Section state is undefined (transient).
-
-t.commit ();
- </pre>
-
- <p>When using change-updated behavior, it is our responsibility to
- mark the section as changed when any of the data members belonging
- to this section is modified. A natural place to mark the section
- as changed is the modifiers for section data members, for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- typedef std::array&lt;char, 1024> key_type;
-
- const key_type&amp;
- public_key () const {return public_key_;}
-
- void
- public_key (const key_type&amp; k)
- {
- public_key_ = k;
- keys_.change ();
- }
-
- const key_type&amp;
- private_key () const {return private_key_;}
-
- void
- private_key (const key_type&amp; k)
- {
- private_key_ = k;
- keys_.change ();
- }
-
-private:
- #pragma db load(lazy) update(change)
- odb::section keys_;
-
- #pragma db section(keys_) type("BLOB")
- key_type public_key_;
-
- #pragma db section(keys_) type("BLOB")
- key_type private_key_;
-
- ...
-};
- </pre>
-
- <p>One interesting aspect of change-updated sections is what happens
- when a transaction that performed an object or section update is
- later rolled back. In this case, while the change state of a
- section has been reset (after update), actual changes were not
- committed to the database. Change-updated sections handle this
- case by automatically registering a rollback callback and then,
- if it is called, restoring the original change state. The
- following code illustrates this semantics (continuing with
- the previous example):</p>
-
- <pre class="cxx">
-auto_ptr&lt;person> p;
-
-try
-{
- transaction t (db.begin ());
- p = db.load&lt;person> (...);
- db.load (*p, p->keys_);
-
- p->private_key (new_key); // The section is marked changed.
- db.update (*p); // The section is reset to unchanged.
-
- throw failed (); // Triggers rollback.
- t.commit ();
-}
-catch (const failed&amp;)
-{
- // The section is restored back to changed.
-}
- </pre>
-
-
- <h2><a name="9.1">9.1 Sections and Inheritance</a></h2>
-
- <p>With both reuse and polymorphism inheritance (<a href="#8">Chapter 8,
- "Inheritance"</a>) it is possible to add new sections to derived
- classes. It is also possible to add data members from derived
- classes to sections declared in the base. For example:</p>
-
- <pre class="cxx">
-#pragma db object polymorphic
-class person
-{
- ...
-
- virtual void
- print ();
-
- #pragma db load(lazy)
- odb::section print_;
-
- #pragma db section(print_)
- std::string bio_;
-};
-
-#pragma db object
-class employee: public person
-{
- ...
-
- virtual void
- print ();
-
- #pragma db section(print_)
- std::vector&lt;std::string> employment_history_;
-};
-
-transaction t (db.begin ());
-
-auto_ptr&lt;person> p (db.load&lt;person> (...)); // Person or employee.
-db.load (*p, p->print_); // Load data members needed for print.
-p->print ();
-
-t.commit ();
- </pre>
-
- <p>When data members of a section are spread over several classes in a
- reuse inheritance hierarchy, both section load and update are
- performed with a single database statement. In contrast, with
- polymorphism inheritance, section load is performed with a
- single statement while update requires a separate statement
- for each class that adds to the section.</p>
-
- <p>Note also that in polymorphism inheritance the section-to-object
- association is static. Or, in other words, you can load a section
- via an object only if its static type actually contains this
- section. The following example will help illustrate this
- point further:</p>
-
- <pre class="cxx">
-#pragma db object polymorphic
-class person
-{
- ...
-};
-
-#pragma db object
-class employee: public person
-{
- ...
-
- #pragma db load(lazy)
- odb::section extras_;
-
- ...
-};
-
-#pragma db object
-class manager: public employee
-{
- ...
-};
-
-auto_ptr&lt;manager> m (db.load&lt;manager> (...));
-
-person&amp; p (*m);
-employee&amp; e (*m);
-section&amp; s (m->extras_);
-
-db.load (p, s); // Error: extras_ is not in person.
-db.load (e, s); // Ok: extras_ is in employee.
- </pre>
-
- <h2><a name="9.2">9.2 Sections and Optimistic Concurrency</a></h2>
-
- <p>When sections are used in a class with the optimistic concurrency
- model (<a href="#12">Chapter 12, "Optimistic Concurrency"</a>),
- both section update and load operations compare the object version
- to that in the database and throw the <code>odb::object_changed</code>
- exception if they do not match. In addition, the section update
- operation increments the version to indicate that the object state
- has changed. For example:</p>
-
- <pre class="cxx">
-#pragma db object optimistic
-class person
-{
- ...
-
- #pragma db version
- unsigned long long version_;
-
- #pragma db load(lazy)
- odb::section extras_;
-
- #pragma db section(extras_)
- std::string bio_;
-};
-
-auto_ptr&lt;person> p;
-
-{
- transaction t (db.begin ());
- p = db.load&lt;person> (...);
- t.commit ();
-}
-
-{
- transaction t (db.begin ());
-
- try
- {
- db.load (*p, p->extras_); // Throws if object state has changed.
- }
- catch (const object_changed&amp;)
- {
- db.reload (*p);
- db.load (*p, p->extras_); // Cannot fail.
- }
-
- t.commit ();
-}
- </pre>
-
- <p>Note also that if an object update triggers one or more
- section updates, then each such update will increment the
- object version. As a result, an update of an object that
- contains sections may result in a version increment by
- more than one.</p>
-
- <p>When sections are used together with optimistic concurrency and
- inheritance, an extra step may be required to enable this
- functionality. If you plan to add new sections to derived
- classes, then the root class of the hierarchy
- (the one that declares the version data member) must be
- declared as sectionable with the <code>db&nbsp;sectionable</code>
- pragma. For example:</p>
-
- <pre class="cxx">
-#pragma db object polymorphic sectionable
-class person
-{
- ...
-
- #pragma db version
- unsigned long long version_;
-};
-
-#pragma db object
-class employee: public person
-{
- ...
-
- #pragma db load(lazy)
- odb::section extras_;
-
- #pragma db section(extras_)
- std::vector&lt;std::string> employment_history_;
-};
- </pre>
-
- <p>This requirement has to do with the need to generate extra
- version increment code in the root class that will be used
- by sections added in the derived classes. If you forget to
- declare the root class as sectionable and later add a
- section to one of the derived classes, the ODB compiler
- will issue diagnostics.</p>
-
- <h2><a name="9.3">9.3 Sections and Lazy Pointers</a></h2>
-
- <p>If a lazy pointer (<a href="#6.4">Section 6.4, "Lazy Pointers"</a>)
- belongs to a lazy-loaded section, then we end up with two levels of
- lazy loading. Specifically, when the section is loaded, the lazy
- pointer is initialized with the object id but the object itself
- is not loaded. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class employee
-{
- ...
-
- #pragma db load(lazy)
- odb::section extras_;
-
- #pragma db section(extras_)
- odb::lazy_shared_ptr&lt;employer> employer_;
-};
-
-transaction t (db.begin ());
-
-auto_ptr&lt;employee> e (db.load&lt;employee> (...)); // employer_ is NULL.
-
-db.load (*e, e->extras_); // employer_ contains valid employer id.
-
-e->employer_.load (); // employer_ points to employer object.
-
-t.commit ();
- </pre>
-
- <h2><a name="9.4">9.4 Sections and Change-Tracking Containers</a></h2>
-
- <p>If a change-tracking container (<a href="#5.4">Section 5.4,
- "Change-Tracking Containers"</a>) belongs to a change-updated
- section, then prior to an object update ODB will check if the
- container has been changed and if so, automatically mark the
- section as changed. For example:</p>
-
-<pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db load(lazy) update(change)
- odb::section extras_;
-
- #pragma db section(extras_)
- odb::vector&lt;std::string> nicknames_;
-};
-
-transaction t (db.begin ());
-
-auto_ptr&lt;person> p (db.load&lt;person> (...));
-db.load (*p, p->extras_);
-
-p->nicknames_.push_back ("JD");
-
-db.update (*p); // Section is automatically updated even
- // though it was not marked as changed.
-t.commit ();
- </pre>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="10">10 Views</a></h1>
-
- <p>An ODB view is a C++ <code>class</code> or <code>struct</code> type
- that embodies a light-weight, read-only projection of one or more
- persistent objects or database tables or the result of a native SQL
- query execution.</p>
-
- <p>Some of the common applications of views include loading a subset
- of data members from objects or columns from database tables, executing
- and handling results of arbitrary SQL queries, including aggregate
- queries and stored procedure calls, as well as joining multiple
- objects and/or database tables using object relationships or custom
- join conditions.</p>
-
- <p>Many relational databases also define the concept of views. Note,
- however, that ODB views are not mapped to database views. Rather,
- by default, an ODB view is mapped to an SQL <code>SELECT</code>
- query. However, if desired, it is easy to create an ODB view
- that is based on a database view.</p>
-
- <p>Usually, views are defined in terms of other persistent entities,
- such as persistent objects, database tables, sequences, etc.
- Therefore, before we can examine our first view, we need to
- define a few persistent objects and a database table. We will
- use this model in examples throughout this chapter. Here we
- assume that you are familiar with ODB object relationship
- support (<a href="#6">Chapter 6, "Relationships"</a>).</p>
-
- <pre class="cxx">
-#pragma db object
-class country
-{
- ...
-
- #pragma db id
- std::string code_; // ISO 2-letter country code.
-
- std::string name_;
-};
-
-#pragma db object
-class employer
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- std::string name_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- std::string first_;
- std::string last_;
-
- unsigned short age_;
-
- shared_ptr&lt;country> residence_;
- shared_ptr&lt;country> nationality_;
-
- shared_ptr&lt;employer> employed_by_;
-};
- </pre>
-
- <p>Besides these objects, we also have the legacy
- <code>employee_extra</code> table that is not mapped to any persistent
- class. It has the following definition:</p>
-
- <pre class="sql">
-CREATE TABLE employee_extra(
- employee_id INTEGER NOT NULL,
- vacation_days INTEGER NOT NULL,
- previous_employer_id INTEGER)
- </pre>
-
- <p>The above persistent objects and database table as well as many of
- the views shown in this chapter are based on the
- <code>view</code> example which can be found in the
- <code>odb-examples</code> package of the ODB distribution.</p>
-
- <p>To declare a view we use the <code>db&nbsp;view</code> pragma,
- for example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee)
-struct employee_name
-{
- std::string first;
- std::string last;
-};
- </pre>
-
- <p>The above example shows one of the simplest views that we can create.
- It has a single associated object (<code>employee</code>) and its
- purpose is to extract the employee's first and last names without
- loading any other data, such as the referenced <code>country</code>
- and <code>employer</code> objects.</p>
-
- <p>Views use the same query facility (<a href="#4">Chapter 4, "Querying
- the Database"</a>) as persistent objects. Because support for queries
- is optional and views cannot be used without this support, you need
- to compile any header that defines a view with the
- <code>--generate-query</code> ODB compiler option.</p>
-
- <p>To query the database for a view we use the
- <code>database::query()</code>, <code>database::query_one()</code>, or
- <code>database::query_value()</code> functions in exactly the same way
- as we would use them to query the database for an object. For example,
- the following code fragment shows how we can find the names of all the
- employees that are younger than 31:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee_name> query;
-typedef odb::result&lt;employee_name> result;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;employee_name> (query::age &lt; 31));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
-{
- const employee_name&amp; en (*i);
- cout &lt;&lt; en.first &lt;&lt; " " &lt;&lt; en.last &lt;&lt; endl;
-}
-
-t.commit ();
- </pre>
-
- <p>A view can be defined as a projection of one or more objects, one
- or more tables, a combination of objects and tables, or it can be
- the result of a custom SQL query. The following sections discuss each
- of these kinds of view in more detail.</p>
-
- <h2><a name="10.1">10.1 Object Views</a></h2>
-
- <p>To associate one or more objects with a view we use the
- <code>db&nbsp;object</code> pragma (<a href="#14.2.1">Section
- 14.2.1, "<code>object</code>"</a>). We have already seen
- a simple, single-object view in the introduction to this chapter.
- To associate the second and subsequent objects we repeat the
- <code>db&nbsp;object</code> pragma for each additional object,
- for example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) object(employer)
-struct employee_employer
-{
- std::string first;
- std::string last;
- std::string name;
-};
- </pre>
-
- <p>The complete syntax of the <code>db&nbsp;object</code> pragma is
- shown below:</p>
-
- <p><code><b>object(</b><i>name</i>
- [<b>=</b> <i>alias</i>]
- [<i>join-type</i>]
- [<b>:</b> <i>join-condition</i>]<b>)</b></code></p>
-
- <p>The <i>name</i> part is a potentially qualified persistent class
- name that has been defined previously. The optional <i>alias</i>
- part gives this object an alias. If provided, the alias is used
- in several contexts instead of the object's unqualified name. We
- will discuss aliases further as we cover each of these contexts
- below. The optional <i>join-type</i> part specifies the way this
- object is associated. It can be <code>left</code>, <code>right</code>,
- <code>full</code>, <code>inner</code>, and <code>cross</code>
- with <code>left</code> being the default.
- Finally, the optional <i>join-condition</i> part provides the
- criteria which should be used to associate this object with any
- of the previously associated objects or, as we will see in
- <a href="#10.4">Section 10.4, "Mixed Views"</a>, tables. Note that
- while the first associated object can have an alias, it cannot
- have a join type or condition.</p>
-
- <p>For each subsequent associated object the ODB compiler needs
- a join condition and there are several ways to specify
- it. The easiest way is to omit it altogether and let the ODB
- compiler try to come up with a join condition automatically.
- To do this the ODB compiler will examine each previously
- associated object for object relationships
- (<a href="#6">Chapter 6, "Relationships"</a>) that
- may exist between these objects and the object being associated.
- If such a relationship exists and is unambiguous, that is
- there is only one such relationship, then the ODB compiler
- will automatically use it to come up with the join condition for
- this object. This is exactly what happens in the previous
- example: there is a single relationship
- (<code>employee::employed_by</code>) between the
- <code>employee</code> and <code>employer</code> objects.</p>
-
- <p>On the other hand, consider this view:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) object(country)
-struct employee_residence
-{
- std::string first;
- std::string last;
- std::string name;
-};
- </pre>
-
- <p>While there is a relationship between <code>country</code> and
- <code>employee</code>, it is ambiguous. It can be
- <code>employee::residence_</code> (which is what we want) or
- it can be <code>employee::nationality_</code> (which we don't
- want). As result, when compiling the above view, the ODB
- compiler will issue an error indicating an ambiguous object
- relationship. To resolve this ambiguity, we can explicitly
- specify the object relationship that should be used to create
- the join condition as the name of the corresponding data member.
- Here is how we can fix the <code>employee_residence</code>
- view:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) object(country: employee::residence_)
-struct employee_residence
-{
- std::string first;
- std::string last;
- std::string name;
-};
- </pre>
-
- <p>It is possible to associate the same object with a single view
- more than once using different join conditions. However, in
- this case, we have to use aliases to assign different names
- for each association. For example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- object(country = res_country: employee::residence_) \
- object(country = nat_country: employee::nationality_)
-struct employee_country
-{
- ...
-};
- </pre>
-
- <p>Note that correctly defining data members in this view requires
- the use of a mechanism that we haven't yet covered. We will
- see how to do this shortly.</p>
-
- <p>If we assign an alias to an object and refer to a data member of
- this object in one of the join conditions, we have to use the
- unqualified alias name instead of the potentially qualified
- object name. For example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee = ee) object(country: ee::residence_)
-struct employee_residence
-{
- ...
-};
- </pre>
-
- <p>The last way to specify a join condition is to provide a custom
- query expression. This method is primarily useful if you would
- like to associate an object using a condition that does not
- involve an object relationship. Consider, for example, a
- modified <code>employee</code> object from the beginning of
- the chapter with an added country of birth member. For one
- reason or another we have decided not to use a relationship to
- the <code>country</code> object, as we have done with
- residence and nationality.</p>
-
- <pre class="cxx">
-#pragma db object
-class employee
-{
- ...
-
- std::string birth_place_; // Country name.
-};
- </pre>
-
- <p>If we now want to create a view that returns the birth country code
- for an employee, then we have to use a custom join condition when
- associating the <code>country</code> object. For example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- object(country: employee::birth_place_ == country::name_)
-struct employee_birth_code
-{
- std::string first;
- std::string last;
- std::string code;
-};
- </pre>
-
- <p>The syntax of the query expression in custom join conditions
- is the same as in the query facility used to query the database
- for objects (<a href="#4">Chapter 4, "Querying the Database"</a>)
- except that for query members, instead of using
- <code>odb::query&lt;object>::member</code> names, we refer directly
- to object members.</p>
-
- <p>Looking at the views we have defined so far, you may be wondering
- how the ODB compiler knows which view data members correspond to which
- object data members. While the names are similar, they are not exactly
- the same, for example <code>employee_name::first</code> and
- <code>employee::first_</code>.</p>
-
- <p>As with join conditions, when it comes to associating data members,
- the ODB compiler tries to do this automatically. It first searches
- all the associated objects for an exact name match. If no match is
- found, then the ODB compiler compares the so-called public names.
- A public name of a member is obtained by removing the common member
- name decorations, such as leading and trailing underscores, the
- <code>m_</code> prefix, etc. In both of these searches the ODB
- compiler also makes sure that the types of the two members are the
- same or compatible.</p>
-
- <p>If one of the above searches returned a match and it is unambiguous, that
- is there is only one match, then the ODB compiler will automatically
- associate the two members. On the other hand, if no match is found
- or the match is ambiguous, the ODB compiler will issue an error.
- To associate two differently-named members or to resolve an ambiguity,
- we can explicitly specify the member association using the
- <code>db&nbsp;column</code> pragma (<a href="#14.4.9">Section 14.4.9,
- "<code>column</code>"</a>). For example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) object(employer)
-struct employee_employer
-{
- std::string first;
- std::string last;
-
- #pragma db column(employer::name_)
- std::string employer_name;
-};
- </pre>
-
- <p>If an object data member specifies the SQL type with
- the <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section
- 14.4.3, "<code>type</code>"</a>), then this type is also used for
- the associated view data members.</p>
-
- <p>Note also that similar to join conditions, if we assign an alias to
- an object and refer to a data member of this object in one of the
- <code>db&nbsp;column</code> pragmas, then we have to use the
- unqualified alias name instead of the potentially qualified
- object name. For example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- object(country = res_country: employee::residence_) \
- object(country = nat_country: employee::nationality_)
-struct employee_country
-{
- std::string first;
- std::string last;
-
- #pragma db column(res_country::name_)
- std::string res_country_name;
-
- #pragma db column(nat_country::name_)
- std::string nat_country_name;
-};
- </pre>
-
- <p>Besides specifying just the object member, we can also specify a
- <em>+-expression</em> in the <code>db&nbsp;column</code> pragma. A
- +-expression consists of string literals and object
- member references connected using the <code>+</code> operator.
- It is primarily useful for defining aggregate views based on
- SQL aggregate functions, for example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee)
-struct employee_count
-{
- #pragma db column("count(" + employee::id_ + ")")
- std::size_t count;
-};
- </pre>
-
- <p>When querying the database for a view, we may want to provide
- additional query criteria based on the objects associated with
- this view. To support this a view defines query members for all
- the associated objects which allows us to refer to such objects'
- members using the <code>odb::query&lt;view>::member</code> expressions.
- This is similar to how we can refer to object members using the
- <code>odb::query&lt;object>::member</code> expressions when
- querying the database for an object. For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee_count> query;
-
-transaction t (db.begin ());
-
-// Find the number of employees with the Doe last name. Result of this
-// aggregate query contains only one element so use the query_value()
-// shortcut function.
-//
-employee_count ec (
- db.query_value&lt;employee_count> (query::last == "Doe"));
-
-cout &lt;&lt; ec.count &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>In the above query we used the last name data member from the associated
- <code>employee</code> object to only count employees with the specific
- name.</p>
-
- <p>When a view has only one associated object, the query members
- corresponding to this object are defined directly in the
- <code>odb::query&lt;view></code> scope. For instance,
- in the above example, we referred to the last name member as
- <code>odb::query&lt;employee_count>::last</code>. However, if
- a view has multiple associated objects, then query members
- corresponding to each such object are defined in a nested
- scope named after the object. As an example, consider
- the <code>employee_employer</code> view again:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) object(employer)
-struct employee_employer
-{
- std::string first;
- std::string last;
-
- #pragma db column(employer::name_)
- std::string employer_name;
-};
- </pre>
-
- <p>Now, to refer to the last name data member from the <code>employee</code>
- object we use the
- <code>odb::query&lt;...>::employee::last</code> expression.
- Similarly, to refer to the employer name, we use the
- <code>odb::query&lt;...>::employer::name</code> expression.
- For example:</p>
-
- <pre class="cxx">
-typedef odb::result&lt;employee_employer> result;
-typedef odb::query&lt;employee_employer> query;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;employee_employer> (
- query::employee::last == "Doe" &amp;&amp;
- query::employer::name == "Simple Tech Ltd"));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
- cout &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->employer_name &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>If we assign an alias to an object, then this alias is used to
- name the query members scope instead of the object name. As an
- example, consider the <code>employee_country</code> view again:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- object(country = res_country: employee::residence_) \
- object(country = nat_country: employee::nationality_)
-struct employee_country
-{
- ...
-};
- </pre>
-
- <p>And a query which returns all the employees that have the same
- country of residence and nationality:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee_country> query;
-typedef odb::result&lt;employee_country> result;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;employee_country> (
- query::res_country::name == query::nat_country::name));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
- cout &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->res_country_name &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>Note also that unlike object query members, view query members do
- no support referencing members in related objects. For example,
- the following query is invalid:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee_name> query;
-typedef odb::result&lt;employee_name> result;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;employee_name> (
- query::employed_by->name == "Simple Tech Ltd"));
-
-t.commit ();
- </pre>
-
- <p>To get this behavior, we would instead need to associate the
- <code>employer</code> object with this view and then use the
- <code>query::employer::name</code> expression instead of
- <code>query::employed_by->name</code>.</p>
-
- <p>As we have discussed above, if specified, an object alias is
- used instead of the object name in the join condition, data
- member references in the <code>db&nbsp;column</code> pragma,
- as well as to name the query members scope. The object alias
- is also used as a table name alias in the underlying
- <code>SELECT</code> statement generated by the ODB compiler.
- Normally, you would not use the table alias directly with
- object views. However, if for some reason you need to refer
- to a table column directly, for example, as part of a native
- query expression, and you need to qualify the column with
- the table, then you will need to use the table alias instead.</p>
-
- <h2><a name="10.2">10.2 Object Loading Views</a></h2>
-
- <p>A special variant of object views is object loading views. Object
- loading views allow us to load one or more complete objects
- instead of, or in addition to, a subset of data member. While we
- can often achieve the same end result by calling
- <code>database::load()</code>, using a view has several advantages.</p>
-
- <p>If we need to load multiple objects, then using a view allows us
- to do this with a single <code>SELECT</code> statement execution
- instead of one for each object that would be necessary in case of
- <code>load()</code>. A view can also be useful for loading only
- a single object if the query criterion that we would like to use
- involves other, potentially unrelated, objects. We will examine
- concrete examples of these and other scenarios in the rest of this
- section.</p>
-
- <p>To load a complete object as part of a view we use a data member of
- the pointer to object type, just like for object relationships
- (<a href="#6">Chapter 6, "Relationships"</a>). As an example, here
- is how we can load both the <code>employee</code> and
- <code>employer</code> objects from the previous section with a single
- statement:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) object(employer)
-struct employee_employer
-{
- shared_ptr&lt;employee> ee;
- shared_ptr&lt;employer> er;
-};
- </pre>
-
- <p>We use an object loading view just like any other view. In the
- result of a query, as we would expect, the pointer data members
- point to the loaded objects. For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee_employer> query;
-
-transaction t (db.begin ());
-
-for (const employee_employer&amp; r:
- db.query&lt;employee_employer> (query::employee::age &lt; 31))
-{
- cout &lt;&lt; r.ee->age () &lt;&lt; " " &lt;&lt; r.er->name () &lt;&lt; endl;
-}
-
-t.commit ();
- </pre>
-
- <p>As another example, consider a query that loads the <code>employer</code>
- objects using some condition based on its employees. For instance, we
- want to find all the employers that employ people over 65 years old.
- We can use this object loading view to implement such a query (notice
- the <code>distinct</code> result modifier discussed later in
- <a href="#10.5">Section 10.5, "View Query Conditions"</a>):</p>
-
- <pre class="cxx">
-#pragma db view object(employer) object(employee) query(distinct)
-struct employer_view
-{
- shared_ptr&lt;employer> er;
-};
- </pre>
-
- <p>And this is how we can use this view to find all the employers that
- employ seniors:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employer_view> query;
-
-db.query&lt;employer_view> (query::employee::age > 65)
- </pre>
-
- <p>We can even use object loading views to load completely unrelated
- (from the ODB object relationships point of view) objects. For example,
- the following view will load all the employers that are named the
- same as a country (notice the <code>inner</code> join type):</p>
-
- <pre class="cxx">
-#pragma db view object(employer) \
- object(country inner: employer::name == country::name)
-struct employer_named_country
-{
- shared_ptr&lt;employer> e;
- shared_ptr&lt;country> c;
-};
- </pre>
-
- <p>An object loading view can contain ordinary data members
- in addition to object pointers. For example, if we are only
- interested in the country code in the above view, then we
- can reimplement it like this:</p>
-
- <pre class="cxx">
-#pragma db view object(employer) \
- object(country inner: employer::name == country::name)
-struct employer_named_country
-{
- shared_ptr&lt;employer> e;
- std::string code;
-};
- </pre>
-
- <p>Object loading views also have a few rules and restrictions.
- Firstly, the pointed-to object in the data member must be associated
- with the view. Furthermore, if the associated object has an alias,
- then the data member name must be the same as the alias (more
- precisely, the public name derived from the data member must
- match the alias; which means we can use normal data member
- decorations such as trailing underscores, etc., see the previous
- section for more information on public names). The following view
- illustrates the use of aliases as data member names:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- object(country = res: employee::residence_) \
- object(country = nat: employee::nationality_)
-struct employee_country
-{
- shared_ptr&lt;country> res;
- shared_ptr&lt;country> nat_;
-};
- </pre>
-
- <p>Finally, the object pointers must be direct data members of
- the view. Using, for example, a composite value that contains
- pointers as a view data member is not supported. Note also
- that depending on the join type you are using, some of the
- resulting pointers might be <code>NULL</code>.</p>
-
- <p>Up until now we have consistently used <code>shared_ptr</code>
- as an object pointer in our views. Can we use other pointers,
- such as <code>unique_ptr</code> or raw pointers? To answer
- this question we first need to discuss what happens with
- object pointers that may be inside objects that a view
- loads. As a concrete example, let us revisit the
- <code>employee_employer</code> view from the beginning of
- this section:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) object(employer)
-struct employee_employer
-{
- shared_ptr&lt;employee> ee;
- shared_ptr&lt;employer> er;
-};
- </pre>
-
- <p>This view loads two objects: <code>employee</code> and
- <code>employer</code>. The <code>employee</code> object,
- however, also contains a pointer to <code>employer</code>
- (see the <code>employed_by_</code> data member). In fact,
- this is the same object that the view loads since <code>employer</code>
- is associated with the view using this same relationship (ODB
- automatically uses it since it is the only one). The correct
- result of loading such a view is then clear: both <code>er</code> and
- <code>er->employed_by_</code> must point to (or share) the
- same instance.</p>
-
- <p>Just like object loading via the <code>database</code> class
- functions, views achieve this correct behavior of only loading
- a single instance of the same object with the help of session's
- object cache (<a href="#11">Chapter 11, "Session"</a>). In fact,
- object loading views enforce this by throwing the
- <code>session_required</code> exception if there is no current
- session and the view loads an object that is also indirectly
- loaded by one of the other objects. The ODB compiler will also
- issue diagnostics if such an object has session support
- disabled (<a href="#14.1.10">Section 14.1.10,
- "<code>session</code>"</a>).</p>
-
- <p>With this understanding we can now provide the correct implementation
- of our transaction that uses the <code>employee_employer</code> view:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee_employer> query;
-
-transaction t (db.begin ());
-odb::session s;
-
-for (const employee_employer&amp; r:
- db.query&lt;employee_employer> (query::employee::age &lt; 31))
-{
- assert (r.ee->employed_by_ == r.er);
- cout &lt;&lt; r.ee->age () &lt;&lt; " " &lt;&lt; r.er->name () &lt;&lt; endl;
-}
-
-t.commit ();
- </pre>
-
- <p>It might seem logical, then, to always load all the objects from
- all the eager relationships with the view. After all, this will
- lead to them all being loaded with a single statement. While
- this is theoretically true, the reality is slightly more nuanced.
- If there is a high probability of the object already have been
- loaded and sitting in the cache, then not loading the object
- as part of the view (and therefore not fetching all its data
- from the database) might result in better performance.</p>
-
- <p>Now we can also answer the question about which pointers we can
- use in object loading views. From the above discussion it should
- be clear that if an object that we are loading is also part of a
- relationship inside another object that we are loading, then we
- should use some form of a shared ownership pointer. If, however,
- there are no relationships involved, as is the case, for example,
- in our <code>employer_named_country</code> and
- <code>employee_country</code> views above, then we can use a
- unique ownership pointer such as <code>unique_ptr</code>.</p>
-
- <p>Note also that your choice of a pointer type can be limited by the
- "official" object pointer type assigned to the object
- (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>).
- For example, if the object pointer type is <code>shared_ptr</code>,
- you will not be able to use <code>unique_ptr</code> to load
- such an object into a view since initializing <code>unique_ptr</code>
- from <code>shared_ptr</code> would be a mistake.</p>
-
- <p>Unless you want to perform your own object cleanup, raw object
- pointers in views are not particularly useful. They do have one
- special semantics, however: If a raw pointer is used as a view
- member, then, before creating a new instance, the implementation
- will check if the member is <code>NULL</code>. If it is not, then
- it is assumed to point to an existing instance and the implementation
- will load the data into it instead of creating a new one. The
- primary use of this special functionality is to implement by-value
- loading with the ability to detect <code>NULL</code> values.</p>
-
- <p>To illustrate this functionality, consider the following view that
- load the employee's residence country by value:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- object(country = res: employee::residence_) transient
-struct employee_res_country
-{
- typedef country* country_ptr;
-
- #pragma db member(res_) virtual(country_ptr) get(&amp;this.res) \
- set(this.res_null = ((?) == nullptr))
-
- country res;
- bool res_null;
-};
- </pre>
-
- <p>Here we are using a virtual data member
- (<a href="#14.4.13">Section 14.4.13, "<code>virtual</code>"</a>) to
- add an object pointer member to the view. Its accessor expression
- returns the pointer to the <code>res</code> member so that
- the implementation can load the data into it. The modifier
- expression checks the passed pointer to initialize the
- <code>NULL</code> value indicator. Here, the two possible
- values that can be passed to the modifier expression are
- the address of the <code>res</code> member that we returned
- earlier from the accessor and <code>NULL</code> (strictly
- speaking, there is a third possibility: the address of an
- object that was found in the session cache).</p>
-
- <p>If we are not interested in the <code>NULL</code> indicator,
- then the above view can simplified to this:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- object(country = res: employee::residence_) transient
-struct employee_res_country
-{
- typedef country* country_ptr;
-
- #pragma db member(res_) virtual(country_ptr) get(&amp;this.res) set()
-
- country res;
-};
- </pre>
-
- <p>That is, we specify an empty modifier expression which leads to
- the value being ignored.</p>
-
- <p>As another example of by-value loading, consider a view that allows
- us to load objects into existing instances that have been allocated
- outside the view:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- object(country = res: employee::residence_) \
- object(country = nat: employee::nationality_)
-struct employee_country
-{
- employee_country (country&amp; r, country&amp; n): res (&amp;r), nat (&amp;n) {}
-
- country* res;
- country* nat;
-};
- </pre>
-
- <p>And here is how we can use this view:</p>
-
- <pre class="cxx">
-typedef odb::result&lt;employee_country> result;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;employee_country> (...);
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
-{
- country res, nat;
- employee_country v (res, nat);
- i.load (v);
-
- if (v.res != nullptr)
- ... // Result is in res.
-
- if (v.nat != nullptr)
- ... // Result is in nat.
-}
-
-t.commit ();
- </pre>
-
- <p>As a final example of the by-value loading, consider the following
- view which implements a slightly more advanced logic: if the object
- is already in the session cache, then it sets the pointer data member
- in the view (<code>er_p</code>) to that. Otherwise, it loads the data
- into the by-value instance (<code>er</code>). We can also check
- whether the pointer data member points to the instance to distinguish
- between the two outcomes. And we can check it for <code>nullptr</code>
- to detect <code>NULL</code> values.</p>
-
- <pre class="cxx">
-#pragma db view object(employer)
-struct employer_view
-{
- // Since we may be getting the pointer as both smart and raw, we
- // need to create a bit of support code to use in the modifier
- // expression.
- //
- void set_er (employer* p) {er_p = p;} // &amp;er or NULL.
- void set_er (shared_ptr&lt;employer> p) {er_p = p.get ();} // From cache.
-
- #pragma db get(&amp;this.er) set(set_er(?))
- employer* er_p;
-
- #pragma db transient
- employer er;
-
- // Return-by-value support (e.g., query_value()).
- //
- employer_view (): er_p (0) {}
- employer_view (const employer_view&amp; x)
- : er_p (x.er_p == &amp;x.er ? &amp;er : x.er_p), er (x.er) {}
-};
- </pre>
-
- <p>We can use object loading views with polymorphic objects
- (<a href="#8.2">Section 8.2, "Polymorphism Inheritance"</a>). Note,
- however, that when loading a derived object via the base pointer
- in a view, a separate statement will be executed to load the
- dynamic part of the object. There is no support for by-value
- loading for polymorphic objects.</p>
-
- <p>We can also use object loading views with objects without id
- (<a href="#14.1.6">Section 14.1.6, "<code>no_id</code>"</a>).
- Note, however, that for such objects, <code>NULL</code> values
- are not automatically detected (since there is no primary key,
- which is otherwise guaranteed to be not <code>NULL</code>, there
- might not be a column on which to base this detection). The
- workaround for this limitation is to load an otherwise not
- <code>NULL</code> column next to the object which will serve
- as an indicator. For example:</p>
-
- <pre class="cxx">
-#pragma db object no_id
-class object
-{
- ...
-
- int n; // NOT NULL
- std::string s;
-};
-
-#include &lt;odb/nullable.hxx>
-
-#pragma db view object(object)
-struct view
-{
-
- odb::nullable&lt;int> n; // If 'n' is NULL, then, logically, so is 'o'.
- unique_ptr&lt;object> o;
-};
- </pre>
-
- <h2><a name="10.3">10.3 Table Views</a></h2>
-
- <p>A table view is similar to an object view except that it is
- based on one or more database tables instead of persistent
- objects. Table views are primarily useful when dealing with
- ad-hoc tables that are not mapped to persistent classes.</p>
-
- <p>To associate one or more tables with a view we use the
- <code>db&nbsp;table</code> pragma (<a href="#14.2.2">Section 14.2.2,
- "<code>table</code>"</a>). To associate the second and subsequent
- tables we repeat the <code>db&nbsp;table</code> pragma for each
- additional table. For example, the following view is based on the
- <code>employee_extra</code> legacy table we have defined at the
- beginning of the chapter.</p>
-
- <pre class="cxx">
-#pragma db view table("employee_extra")
-struct employee_vacation
-{
- #pragma db column("employee_id") type("INTEGER")
- unsigned long employee_id;
-
- #pragma db column("vacation_days") type("INTEGER")
- unsigned short vacation_days;
-};
- </pre>
-
- <p>Besides the table name in the <code>db&nbsp;table</code> pragma
- we also have to specify the column name for each view data
- member. Note that unlike for object views, the ODB compiler
- does not try to automatically come up with column names for
- table views. Furthermore, we cannot use references to object
- members either, since there are no associated objects in table
- views. Instead, the actual column name or column expression
- must be specified as a string literal. The column name can
- also be qualified with a table name either in the
- <code>"table.column"</code> form or, if either a table
- or a column name contains a period, in the
- <code>"table"."column"</code> form. The following example
- illustrates the use of a column expression:</p>
-
- <pre class="cxx">
-#pragma db view table("employee_extra")
-struct employee_max_vacation
-{
- #pragma db column("max(vacation_days)") type("INTEGER")
- unsigned short max_vacation_days;
-};
- </pre>
-
- <p>Both the associated table names and the column names can be qualified
- with a database schema, for example:</p>
-
- <pre class="cxx">
-#pragma db view table("hr.employee_extra")
-struct employee_max_vacation
-{
- #pragma db column("hr.employee_extra.vacation_days") type("INTEGER")
- unsigned short vacation_days;
-};
- </pre>
-
- <p>For more information on database schemas and the format of the
- qualified names, refer to <a href="#14.1.8">Section 14.1.8,
- "<code>schema</code>"</a>.</p>
-
- <p>Note also that in the above examples we specified the SQL type
- for each of the columns to make sure that the ODB compiler
- has knowledge of the actual types as specified in the database
- schema. This is required to obtain correct and optimal
- generated code.</p>
-
-
- <p>The complete syntax of the <code>db&nbsp;table</code> pragma
- is similar to the <code>db&nbsp;object</code> pragma and is shown
- below:</p>
-
- <p><code><b>table("</b><i>name</i><b>"</b>
- [<b>=</b> <b>"</b><i>alias</i><b>"</b>]
- [<i>join-type</i>]
- [<b>:</b> <i>join-condition</i>]<b>)</b></code></p>
-
- <p>The <i>name</i> part is a database table name. The optional
- <i>alias</i> part gives this table an alias. If provided, the
- alias must be used instead of the table whenever a reference
- to a table is used. Contexts where such a reference may
- be needed include the join condition (discussed below),
- column names, and query expressions. The optional <i>join-type</i>
- part specifies the way this table is associated. It can
- be <code>left</code>, <code>right</code>, <code>full</code>,
- <code>inner</code>, and <code>cross</code> with <code>left</code>
- being the default. Finally, the optional <i>join-condition</i>
- part provides the criteria which should be used to associate this
- table with any of the previously associated tables or, as we will see in
- <a href="#10.4">Section 10.4, "Mixed Views"</a>, objects. Note that
- while the first associated table can have an alias, it cannot have
- a join type or condition.</p>
-
- <p>Similar to object views, for each subsequent associated table the
- ODB compiler needs a join condition. However, unlike for object views,
- for table views the ODB compiler does not try to come up with one
- automatically. Furthermore, we cannot use references to object
- members corresponding to object relationships either, since there
- are no associated objects in table views. Instead, for each
- subsequent associated table, a join condition must be
- specified as a custom query expression. While the syntax of the
- query expression is the same as in the query facility used to query
- the database for objects (<a href="#4">Chapter 4, "Querying the
- Database"</a>), a join condition for a table is normally specified
- as a single string literal containing a native SQL query expression.</p>
-
- <p>As an example of a multi-table view, consider the
- <code>employee_health</code> table that we define in addition
- to <code>employee_extra</code>:</p>
-
- <pre class="sql">
-CREATE TABLE employee_health(
- employee_id INTEGER NOT NULL,
- sick_leave_days INTEGER NOT NULL)
- </pre>
-
- <p>Given these two tables we can now define a view that returns both
- the vacation and sick leave information for each employee:</p>
-
- <pre class="cxx">
-#pragma db view table("employee_extra" = "extra") \
- table("employee_health" = "health": \
- "extra.employee_id = health.employee_id")
-struct employee_leave
-{
- #pragma db column("extra.employee_id") type("INTEGER")
- unsigned long employee_id;
-
- #pragma db column("vacation_days") type("INTEGER")
- unsigned short vacation_days;
-
- #pragma db column("sick_leave_days") type("INTEGER")
- unsigned short sick_leave_days;
-};
- </pre>
-
- <p>Querying the database for a table view is the same as for an
- object view except that we can only use native query expressions.
- For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee_leave> query;
-typedef odb::result&lt;employee_leave> result;
-
-transaction t (db.begin ());
-
-unsigned short v_min = ...
-unsigned short l_min = ...
-
-result r (db.query&lt;employee_leave> (
- "vacation_days > " + query::_val(v_min) + "AND" +
- "sick_leave_days > " + query::_val(l_min)));
-
-t.commit ();
- </pre>
-
-
- <h2><a name="10.4">10.4 Mixed Views</a></h2>
-
- <p>A mixed view has both associated objects and tables. As a first
- example of a mixed view, let us improve <code>employee_vacation</code>
- from the previous section to return the employee's first
- and last names instead of the employee id. To achieve this we
- have to associate both the <code>employee</code> object and
- the <code>employee_extra</code> table with the view:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- table("employee_extra" = "extra": "extra.employee_id = " + employee::id_)
-struct employee_vacation
-{
- std::string first;
- std::string last;
-
- #pragma db column("extra.vacation_days") type("INTEGER")
- unsigned short vacation_days;
-};
- </pre>
-
- <p>When querying the database for a mixed view, we can use query members
- for the parts of the query expression that involves object members
- but have to fall back to using the native syntax for the parts that
- involve table columns. For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee_vacation> query;
-typedef odb::result&lt;employee_vacation> result;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;employee_vacation> (
- (query::last == "Doe") + "AND extra.vacation_days &lt;> 0"));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
- cout &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->vacation_days &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>As another example, consider a more advanced view that associates
- two objects via a legacy table. This view allows us to find the
- previous employer name for each employee:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- table("employee_extra" = "extra": "extra.employee_id = " + employee::id_) \
- object(employer: "extra.previous_employer_id = " + employer::id_)
-struct employee_prev_employer
-{
- std::string first;
- std::string last;
-
- // If previous_employer_id is NULL, then the name will be NULL as well.
- // We use the odb::nullable wrapper to handle this.
- //
- #pragma db column(employer::name_)
- odb::nullable&lt;std::string> prev_employer_name;
-};
- </pre>
-
- <h2><a name="10.5">10.5 View Query Conditions</a></h2>
-
- <p>Object, table, and mixed views can also specify an optional query
- condition that should be used whenever the database is queried for
- this view. To specify a query condition we use the
- <code>db&nbsp;query</code> pragma (<a href="#14.2.3">Section 14.2.3,
- "<code>query</code>"</a>).</p>
-
- <p>As an example, consider a view that returns some information about
- all the employees that are over a predefined retirement age.
- One way to implement this would be to define a standard object
- view as we have done in the previous sections and then use a
- query like this:</p>
-
- <pre class="cxx">
-result r (db.query&lt;employee_retirement> (query::age > 50));
- </pre>
-
- <p>The problem with the above approach is that we have to keep
- repeating the <code>query::age > 50</code> expression every
- time we execute the query, even though this expression always
- stays the same. View query conditions allow us to solve this
- problem. For example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) query(employee::age > 50)
-struct employee_retirement
-{
- std::string first;
- std::string last;
- unsigned short age;
-};
- </pre>
-
- <p>With this improvement we can rewrite our query like this:</p>
-
- <pre class="cxx">
-result r (db.query&lt;employee_retirement> ());
- </pre>
-
- <p>But what if we may also need to restrict the result set based on
- some varying criteria, such as the employee's last name? Or, in other
- words, we may need to combine a constant query expression specified
- in the <code>db&nbsp;query</code> pragma with the varying expression
- specified at the query execution time. To allow this, the
- <code>db&nbsp;query</code> pragma syntax supports the use of the special
- <code>(?)</code> placeholder that indicates the position in the
- constant query expression where the runtime expression should be
- inserted. For example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) query(employee::age > 50 &amp;&amp; (?))
-struct employee_retirement
-{
- std::string first;
- std::string last;
- unsigned short name;
-};
- </pre>
-
- <p>With this change we can now use additional query criteria in our
- view:</p>
-
- <pre class="cxx">
-result r (db.query&lt;employee_retirement> (query::last == "Doe"));
- </pre>
-
- <p>The syntax of the expression in a query condition is the same as in
- the query facility used to query the database for objects
- (<a href="#4">Chapter 4, "Querying the Database"</a>) except for
- two differences. Firstly, for query members, instead of
- using <code>odb::query&lt;object>::member</code> names, we refer
- directly to object members, using the object alias instead of the
- object name if an alias was assigned. Secondly, query conditions
- support the special <code>(?)</code> placeholder which can be used
- both in the C++-integrated query expressions as was shown above
- and in native SQL expressions specified as string literals. The
- following view is an example of the latter case:</p>
-
- <pre class="cxx">
-#pragma db view table("employee_extra") \
- query("vacation_days &lt;> 0 AND (?)")
-struct employee_vacation
-{
- ...
-};
- </pre>
-
- <p>Another common use case for query conditions are views with the
- <code>ORDER BY</code> or <code>GROUP BY</code> clause. Such
- clauses are normally present in the same form in every query
- involving such views. As an example, consider an aggregate
- view which calculate the minimum and maximum ages of employees
- for each employer:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) object(employer) \
- query((?) + "GROUP BY" + employer::name_)
-struct employer_age
-{
- #pragma db column(employer::name_)
- std::string employer_name;
-
- #pragma db column("min(" + employee::age_ + ")")
- unsigned short min_age;
-
- #pragma db column("max(" + employee::age_ + ")")
- unsigned short max_age;
-};
- </pre>
-
- <p>The query condition can be optionally followed (or replaced,
- if no constant query expression is needed) by one or more
- <em>result modifiers</em>. Currently supported result modifiers
- are <code>distinct</code> (which is translated to <code>SELECT
- DISTINCT</code>) and <code>for_update</code> (which is translated
- to <code>FOR UPDATE</code> or equivalent for database systems
- that support it). As an example, consider a view that
- allows us to get some information about employers ordered
- by the object id and without any duplicates:</p>
-
- <pre class="cxx">
-#pragma db view object(employer) object(employee) \
- query((?) + "ORDER BY" + employer::name_, distinct)
-struct employer_info
-{
- ...
-};
- </pre>
-
- <p>If we don't require ordering, then this view can be re-implemented
- like this:</p>
-
- <pre class="cxx">
-#pragma db view object(employer) object(employee) query(distinct)
-struct employer_info
-{
- ...
-};
- </pre>
-
- <h2><a name="10.6">10.6 Native Views</a></h2>
-
- <p>The last kind of view supported by ODB is a native view. Native
- views are a low-level mechanism for capturing results of native
- SQL queries, stored procedure calls, etc. Native views don't have
- associated tables or objects. Instead, we use the
- <code>db&nbsp;query</code> pragma to specify the native SQL query,
- which should normally include the select-list and, if applicable,
- the from-list. For example, here is how we can re-implement the
- <code>employee_vacation</code> table view from Section 10.3 above
- as a native view:</p>
-
- <pre class="cxx">
-#pragma db view query("SELECT employee_id, vacation_days " \
- "FROM employee_extra")
-struct employee_vacation
-{
- #pragma db type("INTEGER")
- unsigned long employee_id;
-
- #pragma db type("INTEGER")
- unsigned short vacation_days;
-};
- </pre>
-
- <p>In native views the columns in the query select-list are
- associated with the view data members in the order specified.
- That is, the first column is stored in the first member, the
- second column &mdash; in the second member, and so on. The ODB compiler
- does not perform any error checking in this association. As a result
- you must make sure that the number and order of columns in the
- query select-list match the number and order of data members
- in the view. This is also the reason why we are not
- required to provide the column name for each data member in native
- views, as is the case for object and table views.</p>
-
- <p>Note also that while it is always possible to implement a table
- view as a native view, the table views must be preferred since
- they are safer. In a native view, if you add, remove, or
- rearrange data members without updating the column list in the
- query, or vice versa, at best, this will result in a runtime
- error. In contrast, in a table view such changes will result
- in the query being automatically updated.</p>
-
- <p>Similar to object and table views, the query specified for
- a native view can contain the special <code>(?)</code>
- placeholder which is replaced with the query expression
- specified at the query execution time.
- If the native query does not contain a placeholder, as in
- the example above, then any query expression specified at
- the query execution time is appended to the query text
- along with the <code>WHERE</code> keyword, if required.
- The following example shows the usage of the placeholder:</p>
-
- <pre class="cxx">
-#pragma db view query("SELECT employee_id, vacation_days " \
- "FROM employee_extra " \
- "WHERE vacation_days &lt;> 0 AND (?)")
-struct employee_vacation
-{
- ...
-};
- </pre>
-
- <p>As another example, consider a view that returns the next
- value of a database sequence:</p>
-
- <pre class="cxx">
-#pragma db view query("SELECT nextval('my_seq')")
-struct sequence_value
-{
- unsigned long long value;
-};
- </pre>
-
- <p>While this implementation can be acceptable in some cases, it has
- a number of drawbacks. Firstly, the name of the sequence is
- fixed in the view, which means if we have a second sequence, we
- will have to define another, almost identical view. Similarly,
- the operation that we perform on the sequence is also fixed.
- In some situations, instead of returning the next value, we may
- need the last value.</p>
-
- <p>Note that we cannot use the placeholder mechanism to resolve
- these problems since placeholders can only be used in the
- <code>WHERE</code>, <code>GROUP BY</code>, and similar
- clauses. In other words, the following won't work:</p>
-
- <pre class="cxx">
-#pragma db view query("SELECT nextval('(?)')")
-struct sequence_value
-{
- unsigned long long value;
-};
-
-result r (db.query&lt;sequence_value> ("my_seq"));
- </pre>
-
- <p>To support these kinds of use cases, ODB allows us to specify the
- complete query for a native view at runtime rather than at the view
- definition. To indicate that a native view has a runtime query,
- we can either specify the empty <code>db&nbsp;query</code>
- pragma or omit the pragma altogether. For example:</p>
-
- <pre class="cxx">
-#pragma db view
-struct sequence_value
-{
- unsigned long long value;
-};
- </pre>
-
- <p>Given this view, we can perform the following queries:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;sequence_value> query;
-typedef odb::result&lt;sequence_value> result;
-
-string seq_name = ...
-
-result l (db.query&lt;sequence_value> (
- "SELECT lastval('" + seq_name + "')"));
-
-result n (db.query&lt;sequence_value> (
- "SELECT nextval('" + seq_name + "')"));
- </pre>
-
- <p>Native views can also be used to call and handle results of
- stored procedures. The semantics and limitations of stored
- procedures vary greatly between database systems while some
- do not support this functionality at all. As a result, support
- for calling stored procedures using native views is described
- for each database system in <a href="#II">Part II, "Database
- Systems"</a>.</p>
-
- <h2><a name="10.7">10.7 Other View Features and Limitations</a></h2>
-
- <p>Views cannot be derived from other views. However, you can derive
- a view from a transient C++ class. View data members cannot be
- object pointers. If you need to access data from a pointed-to
- object, then you will need to associate such an object with
- the view. Similarly, view data members cannot be containers.
- These two limitations also apply to composite value types that
- contain object pointers or containers. Such composite values
- cannot be used as view data members.</p>
-
- <p>On the other hand, composite values that do not contain object
- pointers or containers can be used in views. As an example,
- consider a modified version of the <code>employee</code> persistent
- class that stores a person's name as a composite value:</p>
-
- <pre class="cxx">
-#pragma db value
-class person_name
-{
- std::string first_;
- std::string last_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- person_name name_;
-
- ...
-};
- </pre>
-
- <p>Given this change, we can re-implement the <code>employee_name</code>
- view like this:</p>
-
- <pre class="cxx">
-#pragma db view object(employee)
-struct employee_name
-{
- person_name name;
-};
- </pre>
-
- <p>It is also possible to extract some or all of the nested members
- of a composite value into individual view data members. Here is
- how we could have defined the <code>employee_name</code> view
- if we wanted to keep its original structure:</p>
-
- <pre class="cxx">
-#pragma db view object(employee)
-struct employee_name
-{
- #pragma db column(employee::name.first_)
- std::string first;
-
- #pragma db column(employee::name.last_)
- std::string last;
-};
- </pre>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="11">11 Session</a></h1>
-
- <p>A session is an application's unit of work that may encompass several
- database transactions. In this version of ODB a session is just an
- object cache. In future versions it may provide additional
- functionality, such as delayed database operations and automatic
- object state change tracking. As discussed later in
- <a href="#11.2">Section 11.2, "Custom Sessions"</a>, it is also
- possible to provide a custom session implementation that provides
- these or other features.</p>
-
- <p>Session support is optional and can be enabled or disabled on the
- per object basis using the <code>db&nbsp;session</code> pragma, for
- example:</p>
-
- <pre class="cxx">
-#pragma db object session
-class person
-{
- ...
-};
- </pre>
-
- <p>We can also enable or disable session support for a group of
- objects at the namespace level:</p>
-
- <pre class="cxx">
-#pragma db namespace session
-namespace accounting
-{
- #pragma db object // Session support is enabled.
- class employee
- {
- ...
- };
-
- #pragma db object session(false) // Session support is disabled.
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>Finally, we can pass the <code>--generate-session</code> ODB compiler
- option to enable session support by default. With this option session
- support will be enabled for all the persistent classes except those
- for which it was explicitly disabled using the
- <code>db&nbsp;session</code>. An alternative to this method with the
- same effect is to enable session support for the global namespace:</p>
-
- <pre class="cxx">
-#pragma db namespace() session
- </pre>
-
- <p>Each thread of execution in an application can have only one active
- session at a time. A session is started by creating an instance of
- the <code>odb::session</code> class and is automatically terminated
- when this instance is destroyed. You will need to include the
- <code>&lt;odb/session.hxx></code> header file to make this class
- available in your application. For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/database.hxx>
-#include &lt;odb/session.hxx>
-#include &lt;odb/transaction.hxx>
-
-using namespace odb::core;
-
-{
- session s;
-
- // First transaction.
- //
- {
- transaction t (db.begin ());
- ...
- t.commit ();
- }
-
- // Second transaction.
- //
- {
- transaction t (db.begin ());
- ...
- t.commit ();
- }
-
- // Session 's' is terminated here.
-}
- </pre>
-
- <p>The <code>session</code> class has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- class session
- {
- public:
- session (bool make_current = true);
- ~session ();
-
- // Copying or assignment of sessions is not supported.
- //
- private:
- session (const session&amp;);
- session&amp; operator= (const session&amp;);
-
- // Current session interface.
- //
- public:
- static session&amp;
- current ();
-
- static bool
- has_current ();
-
- static void
- current (session&amp;);
-
- static void
- reset_current ();
-
- static session*
- current_pointer ();
-
- static void
- current_pointer (session*);
-
- // Object cache interface.
- //
- public:
- template &lt;typename T>
- struct cache_position {...};
-
- template &lt;typename T>
- cache_position&lt;T>
- cache_insert (database&amp;,
- const object_traits&lt;T>::id_type&amp;,
- const object_traits&lt;T>::pointer_type&amp;);
-
- template &lt;typename T>
- object_traits&lt;T>::pointer_type
- cache_find (database&amp;, const object_traits&lt;T>::id_type&amp;) const;
-
- template &lt;typename T>
- void
- cache_erase (const cache_position&lt;T>&amp;);
-
- template &lt;typename T>
- void
- cache_erase (database&amp;, const object_traits&lt;T>::id_type&amp;);
- };
-}
- </pre>
-
- <p>The session constructor creates a new session and, if the
- <code>make_current</code> argument is <code>true</code>, sets it as a
- current session for this thread. If we try to make a session current
- while there is already another session in effect for this thread,
- then the constructor throws the <code>odb::already_in_session</code>
- exception. The destructor clears the current session for this
- thread if this session is the current one.</p>
-
- <p>The static <code>current()</code> accessor returns the currently active
- session for this thread. If there is no active session, this function
- throws the <code>odb::not_in_session</code> exception. We can check
- whether there is a session in effect in this thread using the
- <code>has_current()</code> static function.</p>
-
- <p>The static <code>current()</code> modifier allows us to set the
- current session for this thread. The <code>reset_current()</code>
- static function clears the current session. These two functions
- allow for more advanced use cases, such as multiplexing
- two or more sessions on the same thread.</p>
-
- <p>The static <code>current_pointer()</code> overloaded functions
- provided the same functionality but using pointers. Specifically,
- the <code>current_pointer()</code> accessor can be used to
- test whether there is a current session and get a pointer to it
- all with a single call.</p>
-
- <p>We normally don't use the object cache interface directly. However,
- it could be useful in some cases, for example, to find out whether
- an object has already been loaded. Note that when calling
- <code>cache_insert()</code>, <code>cache_find()</code>, or
- the second version of <code>cache_erase()</code>, you need to
- specify the template argument (object type) explicitly. It is
- also possible to access the underlying cache data structures
- directly. This can be useful if, for example, you want to
- iterate over the objects store in the cache. Refer to the ODB
- runtime header files for more details on this direct access.</p>
-
- <h2><a name="11.1">11.1 Object Cache</a></h2>
-
- <p>A session is an object cache. Every time a session-enabled object is
- made persistent by calling the <code>database::persist()</code> function
- (<a href="#3.8">Section 3.8, "Making Objects Persistent"</a>), loaded
- by calling the <code>database::load()</code> or
- <code>database::find()</code> function (the pointer-returning overloads
- only; <a href="#3.9">Section 3.9, "Loading Persistent Objects"</a>),
- or loaded by iterating over a query result (<a href="#4.4">Section 4.4,
- "Query Result"</a>), the pointer to the persistent object, in the form
- of the canonical object pointer (<a href="#3.3">Section 3.3, "Object
- and View Pointers"</a>), is stored in the session. For as long as the
- session is in effect, any subsequent calls to load the same object will
- return the cached instance. When an object's state is deleted from the
- database with the <code>database::erase()</code> function
- (<a href="#3.11">Section 3.11, "Deleting Persistent Objects"</a>), the
- cached object pointer is removed from the session. For example:</p>
-
- <pre class="cxx">
-shared_ptr&lt;person> p (new person ("John", "Doe"));
-
-session s;
-transaction t (db.begin ());
-
-unsigned long id (db.persist (p)); // p is cached in s.
-shared_ptr&lt;person> p1 (db.load&lt;person> (id)); // p1 same as p.
-
-t.commit ();
- </pre>
-
-
- <p>The per-object caching policies depend on the object pointer kind
- (<a href="#6.5">Section 6.5, "Using Custom Smart Pointers"</a>).
- Objects with a unique pointer, such as <code>std::auto_ptr</code>
- or <code>std::unique_ptr</code>, as an object pointer are never
- cached since it is not possible to have two such pointers pointing
- to the same object. When an object is persisted via a pointer or
- loaded as a dynamically allocated instance, objects with both raw
- and shared pointers as object pointers are cached. If an object is
- persisted as a reference or loaded into a pre-allocated instance,
- the object is only cached if its object pointer is a raw pointer.</p>
-
- <p>Also note that when we persist an object as a constant reference
- or constant pointer, the session caches such an object as
- unrestricted (non-<code>const</code>). This can lead to undefined
- behavior if the object being persisted was actually created as
- <code>const</code> and is later found in the session cache and
- used as non-<code>const</code>. As a result, when using sessions,
- it is recommended that all persistent objects be created as
- non-<code>const</code> instances. The following code fragment
- illustrates this point:</p>
-
- <pre class="cxx">
-void save (database&amp; db, shared_ptr&lt;const person> p)
-{
- transaction t (db.begin ());
- db.persist (p); // Persisted as const pointer.
- t.commit ();
-}
-
-session s;
-
-shared_ptr&lt;const person> p1 (new const person ("John", "Doe"));
-unsigned long id1 (save (db, p1)); // p1 is cached in s as non-const.
-
-{
- transaction t (db.begin ());
- shared_ptr&lt;person> p (db.load&lt;person> (id1)); // p == p1
- p->age (30); // Undefined behavior since p1 was created const.
- t.commit ();
-}
-
-shared_ptr&lt;const person> p2 (new person ("Jane", "Doe"));
-unsigned long id2 (save (db, p2)); // p2 is cached in s as non-const.
-
-{
- transaction t (db.begin ());
- shared_ptr&lt;person> p (db.load&lt;person> (id2)); // p == p2
- p->age (30); // Ok, since p2 was not created const.
- t.commit ();
-}
- </pre>
-
- <h2><a name="11.2">11.2 Custom Sessions</a></h2>
-
- <p>ODB can use a custom session implementation instead of the
- default <code>odb::session</code>. There could be multiple
- reasons for an application to provide its own session. For
- example, the application may already include a notion of an
- object cache or registry which ODB can re-use. A custom
- session can also provide additional functionality, such as
- automatic change tracking, delayed database operations, or
- object eviction. Finally, the session-per-thread approach used
- by <code>odb::session</code> may not be suitable for all
- applications. For instance, some may need a thread-safe
- session that can be shared among multiple threads. For
- an example of a custom session that implements automatic
- change tracking by keeping original copies of the objects,
- refer to the <code>common/session/custom</code> test
- in the <code>odb-tests</code> package.</p>
-
- <p>To use a custom session we need to specify its type with
- the <code>--session-type</code> ODB compiler command line
- option. We also need to include its definition into the
- generated header file. This can be achieved with the
- <code>--hxx-prologue</code> option. For example, if our
- custom session is called <code>app::session</code> and
- is defined in the <code>app/session.hxx</code> header
- file, then the corresponding ODB compiler options would
- look like this:</p>
-
- <pre class="terminal">
-odb --hxx-prologue "#include \"app/session.hxx\"" \
---session-type ::app::session ...
- </pre>
-
- <p>A custom session should provide the following interface:</p>
-
- <pre class="cxx">
-class custom_session
-{
-public:
- static bool
- _has_cache ();
-
- // Cache management functions.
- //
- template &lt;typename T>
- struct cache_position
- {
- ...
- };
-
- template &lt;typename T>
- static cache_position&lt;T>
- _cache_insert (odb::database&amp;,
- const typename odb::object_traits&lt;T>::id_type&amp;,
- const typename odb::object_traits&lt;T>::pointer_type&amp;);
-
- template &lt;typename T>
- static typename odb::object_traits&lt;T>::pointer_type
- _cache_find (odb::database&amp;,
- const typename odb::object_traits&lt;T>::id_type&amp;);
-
- template &lt;typename T>
- static void
- _cache_erase (const cache_position&lt;T>&amp;);
-
- // Notification functions.
- //
- template &lt;typename T>
- static void
- _cache_persist (const cache_position&lt;T>&amp;);
-
- template &lt;typename T>
- static void
- _cache_load (const cache_position&lt;T>&amp;);
-
- template &lt;typename T>
- static void
- _cache_update (odb::database&amp;, const T&amp; obj);
-
- template &lt;typename T>
- static void
- _cache_erase (odb::database&amp;,
- const typename odb::object_traits&lt;T>::id_type&amp;);
-};
- </pre>
-
- <p>The <code>_has_cache()</code> function shall return <code>true</code>
- if the object cache is in effect in the current thread.</p>
-
- <p>The <code>cache_position</code> class template represents a position
- in the cache of the inserted object. It should be default and
- copy-constructible as well as copy-assignable. The default
- constructor shall create a special empty/<code>NULL</code>
- position. A call of any of the cache management or notification
- functions with such an empty/<code>NULL</code> position shall be
- ignored.</p>
-
- <p>The <code>_cache_insert()</code> function shall add the object into
- the object cache and return its position. The <code>_cache_find()</code>
- function looks an object up in the object cache given its id.
- It returns a <code>NULL</code> pointer if the object is not
- found. The <code>_cache_erase()</code> cache management function
- shall remove the object from the cache. It is called
- if the database operation that caused the object to be inserted
- (for example, load) failed. Note also that after insertion the object
- state is undefined. You can only access the object state
- (for example, make a copy or clear a flag) from one of the
- notification functions discussed below.</p>
-
- <p>The notification functions are called after an object has
- been persisted, loaded, updated, or erased, respectively. If
- your session implementation does not need some of the
- notifications, you still have to provide their functions,
- however, you can leave their implementations empty.</p>
-
- <p>Notice also that all the cache management and notification
- functions are static. This is done in order to allow for a
- custom notion of a current session. Normally, the first
- step a non-empty implementation will perform is lookup the
- current session.</p>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="12">12 Optimistic Concurrency</a></h1>
-
- <p>The ODB transaction model (<a href="#3.5">Section 3.5,
- "Transactions"</a>) guarantees consistency as long as we perform all the
- database operations corresponding to a specific application transaction
- in a single database transaction. That is, if we load an object within a
- database transaction and update it in the same transaction, then we are
- guaranteed that the object state that we are updating in the database is
- exactly the same as the state we have loaded. In other words, it is
- impossible for another process or thread to modify the object state
- in the database between these load and update operations.</p>
-
- <p>In this chapter we use the term <em>application transaction</em>
- to refer to a set of operations on persistent objects that an
- application needs to perform in order to implement some
- application-specific functionality. The term <em>database
- transaction</em> refers to the set of database operations
- performed between the ODB <code>begin()</code> and <code>commit()</code>
- calls. Up until now we have treated application transactions and
- database transactions as essentially the same thing.</p>
-
- <p>While this model is easy to understand and straightforward to use,
- it may not be suitable for applications that have long application
- transactions. The canonical example of such a situation is an
- application transaction that requires user input between loading
- an object and updating it. Such an operation may take an arbitrary
- long time to complete and performing it within a single database
- transaction will consume database resources as well as prevent
- other processes/threads from updating the object for too long.</p>
-
- <p>The solution to this problem is to break up the long-lived
- application transaction into several short-lived database
- transactions. In our example that would mean loading the object
- in one database transaction, waiting for user input, and then
- updating the object in another database transaction. For example:</p>
-
- <pre class="cxx">
-unsigned long id = ...;
-person p;
-
-{
- transaction t (db.begin ());
- db.load (id, p);
- t.commit ();
-}
-
-cerr &lt;&lt; "enter age for " &lt;&lt; p.first () &lt;&lt; " " &lt;&lt; p.last () &lt;&lt; endl;
-unsigned short age;
-cin >> age;
-p.age (age);
-
-{
- transaction t (db.begin ());
- db.update (p);
- t.commit ();
-}
- </pre>
-
- <p>This approach works well if we only have one process/thread that can ever
- update the object. However, if we have multiple processes/threads
- modifying the same object, then this approach does not guarantee
- consistency anymore. Consider what happens in the above example if
- another process updates the person's last name while we are waiting for
- the user input. Since we loaded the object before this change occured,
- our version of the person's data will still have the old name. Once we
- receive the input from the user, we go ahead and update the object,
- overwriting both the old age with the new one (correct) and the new name
- with the old one (incorrect).</p>
-
- <p>While there is no way to restore the consistency guarantee in
- an application transaction that consists of multiple database
- transactions, ODB provides a mechanism, called optimistic
- concurrency, that allows applications to detect and potentially
- recover from such inconsistencies.</p>
-
- <p>In essence, the optimistic concurrency model detects mismatches
- between the current object state in the database and the state
- when it was loaded into the application memory. Such a mismatch
- would mean that the object was changed by another process or
- thread. There are several ways to implement such state mismatch
- detection. Currently, ODB uses object versioning while other
- methods, such as timestamps, may be supported in the future.</p>
-
- <p>To declare a persistent class with the optimistic concurrency model we
- use the <code>optimistic</code> pragma (<a href="#14.1.5">Section 14.1.5,
- "<code>optimistic</code>"</a>). We also use the <code>version</code>
- pragma (<a href="#14.4.16">Section 14.4.16, "<code>version</code>"</a>)
- to specify which data member will store the object version. For
- example:</p>
-
- <pre class="cxx">
-#pragma db object optimistic
-class person
-{
- ...
-
- #pragma db version
- unsigned long version_;
-};
- </pre>
-
- <p>The version data member is managed by ODB. It is initialized to
- <code>1</code> when the object is made persistent and incremented
- by <code>1</code> with each update. The <code>0</code> version value
- is not used by ODB and the application can use it as a special value,
- for example, to indicate that the object is transient. Note that
- for optimistic concurrency to function properly, the application
- should not modify the version member after making the object persistent
- or loading it from the database and until deleting the state of this
- object from the database. To avoid any accidental modifications
- to the version member, we can declare it <code>const</code>, for
- example:</p>
-
- <pre class="cxx">
-#pragma db object optimistic
-class person
-{
- ...
-
- #pragma db version
- const unsigned long version_;
-};
- </pre>
-
- <p>When we call the <code>database::update()</code> function
- (<a href="#3.10">Section 3.10, "Updating Persistent Objects"</a>) and pass
- an object that has an outdated state, the <code>odb::object_changed</code>
- exception is thrown. At this point the application has two
- recovery options: it can abort and potentially restart the
- application transaction or it can reload the new object
- state from the database, re-apply or merge the changes, and call
- <code>update()</code> again. Note that aborting an application
- transaction that performs updates in multiple database transactions
- may require reverting changes that have already been committed to
- the database. As a result, this strategy works best if all the
- updates are performed in the last database transaction of the
- application transaction. This way the changes can be reverted
- by simply rolling back this last database transaction.</p>
-
- <p>The following example shows how we can reimplement the above
- transaction using the second recovery option:</p>
-
- <pre class="cxx">
-unsigned long id = ...;
-person p;
-
-{
- transaction t (db.begin ());
- db.load (id, p);
- t.commit ();
-}
-
-cerr &lt;&lt; "enter age for " &lt;&lt; p.first () &lt;&lt; " " &lt;&lt; p.last () &lt;&lt; endl;
-unsigned short age;
-cin >> age;
-p.age (age);
-
-{
- transaction t (db.begin ());
-
- try
- {
- db.update (p);
- }
- catch (const object_changed&amp;)
- {
- db.reload (p);
- p.age (age);
- db.update (p);
- }
-
- t.commit ();
-}
- </pre>
-
- <p>An important point to note in the above code fragment is that the second
- <code>update()</code> call cannot throw the <code>object_changed</code>
- exception because we are reloading the state of the object
- and updating it within the same database transaction.</p>
-
- <p>Depending on the recovery strategy employed by the application,
- an application transaction with a failed update can be significantly
- more expensive than a successful one. As a result, optimistic
- concurrency works best for situations with low to medium contention
- levels where the majority of the application transactions complete
- without update conflicts. This is also the reason why this concurrency
- model is called optimistic.</p>
-
- <p>In addition to updates, ODB also performs state mismatch detection
- when we are deleting an object from the database
- (<a href="#3.11">Section 3.11, "Deleting Persistent Objects"</a>).
- To understand why this can be important, consider the following
- application transaction:</p>
-
- <pre class="cxx">
-unsigned long id = ...;
-person p;
-
-{
- transaction t (db.begin ());
- db.load (id, p);
- t.commit ();
-}
-
-string answer;
-cerr &lt;&lt; "age is " &lt;&lt; p.age () &lt;&lt; ", delete?" &lt;&lt; endl;
-getline (cin, answer);
-
-if (answer == "yes")
-{
- transaction t (db.begin ());
- db.erase (p);
- t.commit ();
-}
- </pre>
-
- <p>Consider again what happens if another process or thread updates
- the object by changing the person's age while we are waiting for
- the user input. In this case, the user makes the decision based on
- a certain age while we may delete (or not delete) an object that has
- a completely different age. Here is how we can fix this problem
- using optimistic concurrency:</p>
-
- <pre class="cxx">
-unsigned long id = ...;
-person p;
-
-{
- transaction t (db.begin ());
- db.load (id, p);
- t.commit ();
-}
-
-string answer;
-for (bool done (false); !done; )
-{
- if (answer.empty ())
- cerr &lt;&lt; "age is " &lt;&lt; p.age () &lt;&lt; ", delete?" &lt;&lt; endl;
- else
- cerr &lt;&lt; "age changed to " &lt;&lt; p.age () &lt;&lt; ", still delete?" &lt;&lt; endl;
-
- getline (cin, answer);
-
- if (answer == "yes")
- {
- transaction t (db.begin ());
-
- try
- {
- db.erase (p);
- done = true;
- }
- catch (const object_changed&amp;)
- {
- db.reload (p);
- }
-
- t.commit ();
- }
- else
- done = true;
-}
- </pre>
-
- <p>Note that state mismatch detection is performed only if we delete
- an object by passing the object instance to the <code>erase()</code>
- function. If we want to delete an object with the optimistic concurrency
- model regardless of its state, then we need to use the <code>erase()</code>
- function that deletes an object given its id, for example:</p>
-
- <pre class="cxx">
-{
- transaction t (db.begin ());
- db.erase (p.id ());
- t.commit ();
-}
- </pre>
-
- <p>Finally, note that for persistent classes with the optimistic concurrency
- model both the <code>update()</code> function as well as the
- <code>erase()</code> function that accepts an object instance as its
- argument no longer throw the <code>object_not_persistent</code>
- exception if there is no such object in the database. Instead,
- this condition is treated as a change of object state and the
- <code>object_changed</code> exception is thrown instead.</p>
-
- <p>For complete sample code that shows how to use optimistic
- concurrency, refer to the <code>optimistic</code> example in
- the <code>odb-examples</code> package.</p>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="13">13 Database Schema Evolution</a></h1>
-
- <p>When we add new persistent classes or change the existing ones, for
- example, by adding or deleting data members, the database schema
- necessary to store the new object model changes as well. At the
- same time, we may have existing databases that contain existing data.
- If new versions of your application don't need to handle
- old databases, then the schema creating functionality is all that
- you need. However, most applications will need to work with data
- stored by older versions of the same application.</p>
-
- <p>We will call <em>database schema evolution</em> the overall task
- of updating the database to match the changes in the object model.
- Schema evolution usually consists of two sub-tasks: <em>schema
- migration</em> and <em>data migration</em>. Schema migration
- modifies the database schema to correspond to the current
- object model. In a relational database, this, for example, could
- require adding or dropping tables and columns. The data migration
- task involves converting the data stored in the existing database
- from the old format to the new one.</p>
-
- <p>If performed manually, database schema evolution is a tedious and
- error-prone task. As a result, ODB provides comprehensive support
- for automated or, more precisely, semi-automated schema
- evolution. Specifically, ODB does fully-automatic schema
- migration and provides facilities to help you with data
- migration.</p>
-
- <p>The topic of schema evolution is a complex and sensitive
- issue since normally there would be valuable, production data at
- stake. As a result, the approach taken by ODB is to provide simple
- and bullet-proof elementary building blocks (or migration steps)
- that we can understand and trust. Using these elementary blocks we
- can then implement more complex migration scenarios. In particular,
- ODB does not try to handle data migration automatically since in most
- cases this requires understanding of application-specific semantics.
- In other words, there is no magic.</p>
-
- <p>There are two general approaches to working with older data: the
- application can either convert it to correspond to the new format
- or it can be made capable of working with multiple versions of this
- format. There is also a hybrid approach where the application
- may convert the data to the new format gradually as part of its
- normal functionality. ODB is capable of handling all these
- scenarios. That is, there is support for working with older
- models without performing any migration (schema or data).
- Alternatively, we can migrate the schema after
- which we have the choice of either also immediately migrating the
- data (<em>immediate data migration</em>) or doing it gradually
- (<em>gradual data migration</em>).</p>
-
- <p>Schema evolution is already a complex task and we should not
- unnecessarily use a more complex approach where a simpler one
- would be sufficient. From the above, the simplest approach is
- the immediate schema migration that does not require any data
- migration. An example of such a change would be adding a new
- data member with the default value (<a href="#14.3.4">Section
- 14.3.4, "<code>default</code>"</a>). This case ODB can handle
- completely automatically.</p>
-
- <p>If we do require data migration, then the next simplest approach
- is the immediate schema and data migration. Here we have to write
- custom migration code. However, it is separate from the rest of
- the core application logic and is executed at a well defined point
- (database migration). In other words, the core application logic
- need not be aware of older model versions. The potential drawback
- of this approach is performance. It may take a lot of resources
- and/or time to convert all the data upfront.</p>
-
- <p>If the immediate migration is not possible, then the next option
- is the immediate schema migration followed by the gradual data
- migration. With this approach, both old and new data must co-exist
- in the new database. We also have to change the application
- logic to both account for different sources of the same data (for
- example, when either an old or new version of the object is loaded)
- as well as migrate the data when appropriate (for example, when
- the old version of the object is updated). At some point, usually
- when the majority of the data has been converted, gradual migrations
- are terminated with an immediate migration.</p>
-
- <p>The most complex approach is working with multiple versions of
- the database without performing any migrations, schema or data.
- ODB does provide support for implementing this approach
- (<a href="#13.4">Section 13.4, "Soft Object Model Changes"</a>),
- however we will not cover it any further in this chapter.
- Generally, this will require embedding knowledge about each
- version into the core application logic which makes it hard
- to maintain for any non-trivial object model.</p>
-
- <p>Note also that when it comes to data migration, we can use
- the immediate variant for some changes and gradual for others.
- We will discuss various migration scenarios in greater detail
- in section <a href="#13.3">Section 13.3, "Data Migration"</a>.</p>
-
- <h2><a name="13.1">13.1 Object Model Version and Changelog</a></h2>
-
- <p>To enable schema evolution support in ODB we need to specify
- the object model version, or, more precisely, two versions.
- The first is the base model version. It is the lowest
- version from which we will be able to migrate. The second
- version is the current model version. In ODB we can migrate
- from multiple previous versions by successively migrating
- from one to the next until we reach the current version.
- We use the <code>db&nbsp;model&nbsp;version</code> pragma
- to specify both the base and current versions.</p>
-
- <p>When we enable schema evolution for the first time, our
- base and current versions will be the same, for example:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 1)
- </pre>
-
- <p>Once we release our application, its users may create databases
- with the schema corresponding to this version of the object
- model. This means that if we make any modifications to our
- object model that also change the schema, then we will need
- to be able to migrate the old databases to this new schema.
- As a result, before making any new changes after a release,
- we increment the current version, for example:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 2)
- </pre>
-
- <p>To put this another way, we can stay on the same version
- during development and keep adding new changes to it. But
- once we release it, any new changes to the object model will
- have to be done in a new version.</p>
-
- <p>It is easy to forget to increment the version before
- making new changes to the object model. To help solve this
- problem, the <code>db&nbsp;model&nbsp;version</code> pragma
- accepts a third optional argument that specify whether the
- current version is open or closed for changes. For example:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 2, open) // Can add new changes to
- // version 2.
- </pre>
-
- <pre class="cxx">
-#pragma db model version(1, 2, closed) // Can no longer add new
- // changes to version 2.
- </pre>
-
- <p>If the current version is closed, ODB will refuse to accept
- any new schema changes. In this situation you would
- normally increment the current version and mark it as open
- or you could re-open the existing version if, for example,
- you need to fix something. Note, however, that re-opening
- versions that have been released will most likely result
- in migration malfunctions. By default the version is open.</p>
-
- <p>Normally, an application will have a range of older database
- versions from which it is able to migrate. When we change
- this range by removing support for older versions, we also
- need to adjust the base model version. This will make sure
- that ODB does not keep unnecessary information around.</p>
-
- <p>A model version (both base and current) is a 64-bit unsigned
- integer (<code>unsigned&nbsp;long&nbsp;long</code>). <code>0</code>
- is reserved to signify special situations, such as the lack of
- schema in the database. Other than that, we can use any values
- as versions as long as they are monotonically increasing. In
- particular, we don't have to start with version <code>1</code>
- and can increase the versions by any increment.</p>
-
- <p>One versioning approach is to use an independent
- object model version by starting from version <code>1</code>
- and also incrementing by <code>1</code>. The alternative
- is to make the model version correspond to the application
- version. For example, if our application is using the
- <code>X.Y.Z</code> version format, then we could encode it
- as a hexadecimal number and use that as our model version,
- for example:</p>
-
- <pre class="cxx">
-#pragma db model version(0x020000, 0x020306) // 2.0.0-2.3.6
- </pre>
-
- <p>Most real-world object models will be spread over multiple
- header files and it will be burdensome to repeat the
- <code>db&nbsp;model&nbsp;version</code> pragma in each of
- them. The recommended way to handle this situation is to
- place the <code>version</code> pragma into a separate header
- file and include it into the object model files. If your
- project already has a header file that defines the
- application version, then it is natural to place this
- pragma there. For example:</p>
-
- <pre class="cxx">
-// version.hxx
-//
-// Define the application version.
-//
-
-#define MYAPP_VERSION 0x020306 // 2.3.6
-
-#ifdef ODB_COMPILER
-#pragma db model version(1, 7)
-#endif
- </pre>
-
- <p>Note that we can also use macros in the <code>version</code>
- pragma which allows us to specify all the versions in a single
- place. For example:</p>
-
- <pre class="cxx">
-#define MYAPP_VERSION 0x020306 // 2.3.6
-#define MYAPP_BASE_VERSION 0x020000 // 2.0.0
-
-#ifdef ODB_COMPILER
-#pragma db model version(MYAPP_BASE_VERSION, MYAPP_VERSION)
-#endif
- </pre>
-
- <p>It is also possible to have multiple object models within the
- same application that have different versions. Such models
- must be independent, that is, no headers from one model shall
- include a header from another. You will also need to assign
- different schema names to each model with the
- <code>--schema-name</code> ODB compiler option.</p>
-
- <p>Once we specify the object model version, the ODB compiler
- starts tracking database schema changes in a changelog file.
- Changelog has an XML-based, line-oriented format. It uses
- XML in order to provide human readability while also
- facilitating, if desired, processing and analysis with
- custom tools. The line orientation makes it easy to review
- with tools like <code>diff</code>.</p>
-
- <p>The changelog is maintained by the ODB compiler. Specifically,
- you do not need to make any manual changes to this file. You
- will, however, need to keep it around from one invocation of
- the ODB compiler to the next. In other words, the changelog
- file is both the input and the output of the ODB compiler. This,
- for example, means that if your project's source code is stored
- in a version control repository, then you will most likely want
- to store the changelog there as well. If you delete the changelog,
- then any ability to do schema migration will be lost.</p>
-
- <p>The only operation that you may want to perform with the
- changelog is to review the database schema changes that resulted
- from the C++ object model changes. For this you can use a tool
- like <code>diff</code> or, better yet, the change review facilities
- offered by your revision control system. For this purpose the
- contents of a changelog will be self-explanatory.</p>
-
- <p>As an example, consider the following initial object model:</p>
-
- <pre class="cxx">
-// person.hxx
-//
-
-#include &lt;string>
-
-#pragma db model version(1, 1)
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-
- std::string first_;
- std::string last_;
-};
- </pre>
-
- <p>We then compile this header file with the ODB compiler (using the
- PostgreSQL database as an example):</p>
-
- <pre class="terminal">
-odb --database pgsql --generate-schema person.hxx
- </pre>
-
- <p>If we now look at the list of generated files, then in addition to
- the now familiar <code>person-odb.?xx</code> and <code>person.sql</code>,
- we will also see <code>person.xml</code> &mdash; the changelog file.
- Just for illustration, below are the contents of this changelog.</p>
-
- <pre class="xml">
-&lt;changelog database="pgsql">
- &lt;model version="1">
- &lt;table name="person" kind="object">
- &lt;column name="id" type="BIGINT" null="false"/>
- &lt;column name="first" type="TEXT" null="false"/>
- &lt;column name="last" type="TEXT" null="false"/>
- &lt;primary-key auto="true">
- &lt;column name="id"/>
- &lt;/primary-key>
- &lt;/table>
- &lt;/model>
-&lt;/changelog>
- </pre>
-
- <p>Let's say we now would like to add another data member to the
- <code>person</code> class &mdash; the middle name. We increment
- the version and make the change:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 2)
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-
- std::string first_;
- std::string middle_;
- std::string last_;
-};
- </pre>
-
- <p>We use exactly the same command line to re-compile our file:</p>
-
- <pre class="terminal">
-odb --database pgsql --generate-schema person.hxx
- </pre>
-
- <p>This time the ODB compiler will read the old changelog, update
- it, and write out the new version. Again, for illustration only,
- below are the updated changelog contents:</p>
-
- <pre class="xml">
-&lt;changelog database="pgsql">
- &lt;changeset version="2">
- &lt;alter-table name="person">
- &lt;add-column name="middle" type="TEXT" null="false"/>
- &lt;/alter-table>
- &lt;/changeset>
-
- &lt;model version="1">
- &lt;table name="person" kind="object">
- &lt;column name="id" type="BIGINT" null="false"/>
- &lt;column name="first" type="TEXT" null="false"/>
- &lt;column name="last" type="TEXT" null="false"/>
- &lt;primary-key auto="true">
- &lt;column name="id"/>
- &lt;/primary-key>
- &lt;/table>
- &lt;/model>
-&lt;/changelog>
- </pre>
-
- <p>Just to reiterate, while the changelog may look like it could
- be written by hand, it is maintained completely automatically
- by the ODB compiler and the only reason you may want to look
- at its contents is to review the database schema changes. For
- example, if we compare the above two changelogs with
- <code>diff</code>, we will get the following summary of the
- database schema changes:</p>
-
- <pre class="xml">
---- person.xml.orig
-+++ person.xml
-@@ -1,4 +1,10 @@
-&lt;changelog database="pgsql">
-<span style="color: #009E00">+ &lt;changeset version="2">
-+ &lt;alter-table name="person">
-+ &lt;add-column name="middle" type="TEXT" null="false"/>
-+ &lt;/alter-table>
-+ &lt;/changeset>
-+</span>
- &lt;model version="1">
- &lt;table name="person" kind="object">
- &lt;column name="id" type="BIGINT" null="false"/>
- </pre>
-
- <p>The changelog is only written when we generate the database schema,
- that is, the <code>--generate-schema</code> option is specified.
- Invocations of the ODB compiler that only produce the database
- support code (C++) do not read or update the changelog. To put it
- another way, the changelog tracks changes in the resulting database
- schema, not the C++ object model.</p>
-
- <p>ODB ignores column order when comparing database schemas. This means
- that we can re-order data members in a class without causing any
- schema changes. Member renames, however, will result in schema
- changes since the column name changes as well (unless we specified
- the column name explicitly). From ODB's perspective such a rename
- looks like the deletion of one data member and the addition of
- another. If we don't want this to be treated as a schema change,
- then we will need to keep the old column name by explicitly
- specifying it with the <code>db&nbsp;column</code> pragma. For
- example, here is how we can rename <code>middle_</code> to
- <code>middle_name_</code> without causing any schema changes:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 2)
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db column("middle") // Keep the original column name.
- std::string middle_name_;
-
- ...
-};
- </pre>
-
- <p>If your object model consists of a large number of header files and
- you generate the database schema for each of them individually, then
- a changelog will be created for each of your header files. This may
- be what you want, however, the large number of changelogs can quickly
- become unwieldy. In fact, if you are generating the database schema
- as standalone SQL files, then you may have already experienced a
- similar problem caused by a large number of <code>.sql</code> files,
- one for each header.</p>
-
- <p>The solution to both of these problems is to generate a combined
- database schema file and a single changelog. For example, assume
- we have three header files in our object model:
- <code>person.hxx</code>, <code>employee.hxx</code>, and
- <code>employer.hxx</code>. To generate the database support code
- we compile them as usual but without specifying the
- <code>--generate-schema</code> option. In this case no changelog
- is created or updated:</p>
-
- <pre class="terminal">
-odb --database pgsql person.hxx
-odb --database pgsql employee.hxx
-odb --database pgsql employer.hxx
- </pre>
-
- <p>To generate the database schema, we perform a separate invocation
- of the ODB compiler. This time, however, we instruct it to only
- generate the schema (<code>--generate-schema-only</code>) and
- produce it combined (<code>--at-once</code>) for all the files
- in our object model:</p>
-
- <pre class="terminal">
-odb --database pgsql --generate-schema-only --at-once \
---input-name company person.hxx employee.hxx employer.hxx
- </pre>
-
- <p>The result of the above command is a single <code>company.sql</code>
- file (the name is derived from the <code>--input-name</code> value)
- that contains the database schema for our entire object model. There
- is also a single corresponding changelog file &mdash;
- <code>company.xml</code>.</p>
-
- <p>The same can be achieved for the embedded schema by instructing
- the ODB compiler to generate the database creation code into a
- separate C++ file (<code>--schema-format&nbsp;separate</code>):</p>
-
- <pre class="terminal">
-odb --database pgsql --generate-schema-only --schema-format separate \
---at-once --input-name company person.hxx employee.hxx employer.hxx
- </pre>
-
- <p>The result of this command is a single <code>company-schema.cxx</code>
- file and, again, <code>company.xml</code>.</p>
-
- <p>Note also that by default the changelog file is not placed into
- the directory specified with the <code>--output-dir</code> option.
- This is due to the changelog being both an input and an output file
- at the same time. As a result, by default, the ODB compiler will
- place it in the directory of the input header file.</p>
-
- <p>There is, however, a number of command line options (including
- <code>--changelog-dir</code>) that allow us to fine-tune the name and
- location of the changelog file. For example, you can instruct the ODB
- compiler to read the changelog from one file while writing it to
- another. This, for example, can be useful if you want to review
- the changes before discarding the old file. For more information
- on these options, refer to the
- <a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
- Compiler Command Line Manual</a> and search for "changelog".</p>
-
- <p>When we were discussing version increments above, we used the
- terms <em>development</em> and <em>release</em>. Specifically,
- we talked about keeping the same object model versions during
- development periods and incrementing them after releases.
- What is a development period and a release in this context?
- These definitions can vary from project to project.
- Generally, during a development period we work on one or
- more changes to the object model that result in the changes
- to the database schema. A release is a point where we
- make our changes available to someone else who may have an
- older database to migrate from. In the traditional sense, a release
- is a point where you make a new version of your application available
- to its users. However, for schema evolution purposes, a release
- could also mean simply making your schema-altering changes
- available to other developers on your team. Let us consider
- two common scenarios to illustrate how all this fits together.</p>
-
- <p>One way to setup a project would be to re-use the application
- development period and application release for schema evolution.
- That is, during a new application version development we keep
- a single object model version and when we release the application,
- we increment the model version. In this case it makes sense to
- also reuse the application version as a model version for
- consistency. Here is a step-by-step guide for this setup:</p>
-
- <ol>
- <li>During development, keep the current object model version open.</li>
-
- <li>Before the release (for example, when entering a "feature freeze")
- close the version.</li>
-
- <li>After the release, update the version and open it.</li>
-
- <li>For each new feature, review the changeset at the top of the
- changelog, for example, with <code>diff</code> or your
- version control facilities. If you are using a version
- control, then this is best done just before committing
- your changes to the repository.</li>
- </ol>
-
- <p>An alternative way to setup schema versioning in a project would
- be to define the development period as working on a single
- feature and the release as making this feature available to
- other people (developers, testers, etc.) on your team, for
- example, by committing the changes to a public version control
- repository. In this case, the object model version will be
- independent of the application version and can simply be
- a sequence that starts with <code>1</code> and is
- incremented by <code>1</code>. Here is a step-by-step guide
- for this setup:</p>
-
- <ol>
- <li>Keep the current model version closed. Once a change is made
- that affects the database schema, the ODB compiler will refuse
- to update the changelog.</li>
-
- <li>If the change is legitimate, open a new version, that is,
- increment the current version and make it open.</li>
-
- <li>Once the feature is implemented and tested, review the final
- set of database changes (with <code>diff</code> or your
- version control facilities), close the version, and commit
- the changes to the version control repository (if using).</li>
- </ol>
-
- <p>If you are using a version control repository that supports
- pre-commit checks, then you may want to consider adding such
- a check to make sure the committed version is always closed.</p>
-
- <p>If we are just starting schema evolution in our project, which
- approach should we choose? The two approaches will work better
- in different situations since they have a different set of
- advantages and disadvantages. The first approach, which we
- can call version per application release, is best suited
- for simpler projects with smaller releases since otherwise
- a single migration will bundle a large number of unrelated
- actions corresponding to different features. This can
- become difficult to review and, if things go wrong, debug.</p>
-
- <p>The second approach, which we can call version per feature,
- is much more modular and provides a number of additional benefits.
- We can perform migrations for each feature as a discreet step
- which makes it easier to debug. We can also place each such
- migration step into a separate transaction further improving
- reliability. It also scales much better in larger teams
- where multiple developers can work concurrently on features
- that affect the database schema. For example, if you find
- yourself in a situation where another developer on your
- team used the same version as you and managed to commit his
- changes before you (that is, you have a merge conflict),
- then you can simply change the version to the next available
- one, regenerate the changelog, and continue with your commit.</p>
-
- <p>Overall, unless you have strong reasons to prefer the version
- per application release approach, rather choose version per
- feature even though it may seem more complex at the
- beginning. Also, if you do select the first approach, consider
- provisioning for switching to the second method by reserving
- a sub-version number. For example, for an application version
- in the form <code>2.3.4</code> you can make the object model
- version to be in the form <code>0x0203040000</code>, reserving
- the last two bytes for a sub-version. Later on you can use it to
- switch to the version per feature approach.</p>
-
- <h2><a name="13.2">13.2 Schema Migration</a></h2>
-
- <p>Once we enable schema evolution by specifying the object model
- version, in addition to the schema creation statements, the
- ODB compiler starts generating schema migration statements
- for each version all the way from the base to the current.
- As with schema creation, schema migration can be generated
- either as a set of SQL files or embedded into the generated
- C++ code (<code>--schema-format</code> option).</p>
-
- <p>For each migration step, that is from one version to the next,
- ODB generates two sets of statements: pre-migration and
- post-migration. The pre-migration statements <em>"relax"</em>
- the database schema so that both old and new data can co-exist.
- At this stage new columns and tables are added while old
- constraints are dropped. The post-migration statements
- <em>"tighten"</em> the database schema back so that only
- data conforming to the new format can remain. At this stage
- old columns and tables are dropped and new constraints are
- added. Now you can probably guess where the data
- migration fits into this &mdash; between the pre and post
- schema migrations where we can both access the old data
- and create the new one.</p>
-
- <p>If the schema is being generated as standalone SQL files,
- then we end up with a pair of files for each step: the pre-migration
- file and the post-migration file. For the <code>person</code>
- example we started in the previous section we will have the
- <code>person-002-pre.sql</code> and <code>person-002-post.sql</code>
- files. Here <code>002</code> is the version <em>to</em> which
- we are migrating while the <code>pre</code> and <code>post</code>
- suffixes specify the migration stage. So if we wanted to migrate
- a <code>person</code> database from version <code>1</code>
- to <code>2</code>, then we would first execute
- <code>person-002-pre.sql</code>, then migrate the data, if any
- (discussed in more detail in the next section), and finally
- execute <code>person-002-post.sql</code>. If our database is
- several versions behind, for example the database has version
- <code>1</code> while the current version is <code>5</code>,
- then we simply perform this set of steps for each version
- until we reach the current version.</p>
-
- <p>If we look at the contents of the <code>person-002-pre.sql</code>
- file, we will see the following (or equivalent, depending on the
- database used) statement:</p>
-
- <pre class="sql">
-ALTER TABLE "person"
- ADD COLUMN "middle" TEXT NULL;
- </pre>
-
- <p>As we would expect, this statement adds a new column corresponding
- to the new data member. An observant reader would notice,
- however, that the column is added as <code>NULL</code>
- even though we never requested this semantics in our object model.
- Why is the column added as <code>NULL</code>? If during migration
- the <code>person</code> table already contains rows (that is, existing
- objects), then an attempt to add a non-<code>NULL</code> column that
- doesn't have a default value will fail. As a result, ODB will initially
- add a new column that doesn't have a default value as <code>NULL</code>
- but then clean this up at the post-migration stage. This way your data
- migration code is given a chance to assign some meaningful values for
- the new data member for all the existing objects. Here are the contents
- of the <code>person-002-post.sql</code> file:</p>
-
- <pre class="sql">
-ALTER TABLE "person"
- ALTER COLUMN "middle" SET NOT NULL;
- </pre>
-
- <p>Currently ODB directly supports the following elementary database
- schema changes:</p>
-
- <ul class="list">
- <li>add table</li>
- <li>drop table</li>
- <li>add column</li>
- <li>drop column</li>
- <li>alter column, set <code>NULL</code>/<code>NOT NULL</code></li>
- <li>add foreign key</li>
- <li>drop foreign key</li>
- <li>add index</li>
- <li>drop index</li>
- </ul>
-
- <p>More complex changes can normally be implemented in terms of
- these building blocks. For example, to change a type of a
- data member (which leads to a change of a column type), we
- can add a new data member with the desired type (add column),
- migrate the data, and then delete the old data member (drop
- column). ODB will issue diagnostics for cases that are
- currently not supported directly. Note also that some database
- systems (notably SQLite) have a number of limitations in their
- support for schema changes. For more information on these
- database-specific limitations, refer to the "Limitations" sections
- in <a href="#II">Part II, "Database Systems"</a>.</p>
-
- <p>How do we know what the current database version is? That is, the
- version <em>from</em> which we need to migrate? We need to know this,
- for example, in order to determine the set of migrations we have to
- perform. By default, when schema evolution is enabled, ODB maintains
- this information in a special table called <code>schema_version</code>
- that has the following (or equivalent, depending on the database
- used) definition:</p>
-
- <pre class="sql">
-CREATE TABLE "schema_version" (
- "name" TEXT NOT NULL PRIMARY KEY,
- "version" BIGINT NOT NULL,
- "migration" BOOLEAN NOT NULL);
- </pre>
-
- <p>The <code>name</code> column is the schema name as specified with
- the <code>--schema-name</code> option. It is empty for the default
- schema. The <code>version</code> column contains the current database
- version. And, finally, the <code>migration</code> flag indicates
- whether we are in the process of migrating the database, that is,
- between the pre and post-migration stages.</p>
-
- <p>The schema creation statements (<code>person.sql</code> in our case)
- create this table and populate it with the initial model version. For
- example, if we executed <code>person.sql</code> corresponding to
- version <code>1</code> of our object model, then <code>name</code>
- would have been empty (which signifies the default schema since we
- didn't specify <code>--schema-name</code>), <code>version</code> will
- be <code>1</code> and <code>migration</code> will be
- <code>FALSE</code>.</p>
-
- <p>The pre-migration statements update the version and set the migration
- flag to <code>TRUE</code>. Continuing with our example, after executing
- <code>person-002-pre.sql</code>, <code>version</code> will
- become <code>2</code> and <code>migration</code> will be set to
- <code>TRUE</code>. The post-migration statements simply clear the
- migration flag. In our case, after running
- <code>person-002-post.sql</code>, <code>version</code> will
- remain <code>2</code> while <code>migration</code> will be reset
- to <code>FALSE</code>.</p>
-
- <p>Note also that above we mentioned that the schema creation statements
- (<code>person.sql</code>) create the <code>schema_version</code> table.
- This means that if we enable schema evolution support in the middle
- of a project, then we could already have existing databases that
- don't include this table. As a result, ODB will not be able to handle
- migrations for such databases unless we manually add the
- <code>schema_version</code> table and populate it with the correct
- version information. For this reason, it is highly recommended that
- you consider whether to use schema evolution and, if so, enable it
- from the beginning of your project.</p>
-
- <p>The <code>odb::database</code> class provides an API for accessing
- and modifying the current database version:</p>
-
- <pre class="cxx">
-namespace odb
-{
- typedef unsigned long long schema_version;
-
- struct LIBODB_EXPORT schema_version_migration
- {
- schema_version_migration (schema_version = 0,
- bool migration = false);
-
- schema_version version;
- bool migration;
-
- // This class also provides the ==, !=, &lt;, >, &lt;=, and >= operators.
- // Version ordering is as follows: {1,f} &lt; {2,t} &lt; {2,f} &lt; {3,t}.
- };
-
- class database
- {
- public:
- ...
-
- schema_version
- schema_version (const std::string&amp; name = "") const;
-
- bool
- schema_migration (const std::string&amp; name = "") const;
-
- const schema_version_migration&amp;
- schema_version_migration (const std::string&amp; name = "") const;
-
- // Set schema version and migration state manually.
- //
- void
- schema_version_migration (schema_version,
- bool migration,
- const std::string&amp; name = "");
-
- void
- schema_version_migration (const schema_version_migration&amp;,
- const std::string&amp; name = "");
-
- // Set default schema version table for all schemas.
- //
- void
- schema_version_table (const std::string&amp; table_name);
-
- // Set schema version table for a specific schema.
- //
- void
- schema_version_table (const std::string&amp; table_name,
- const std::string&amp; name);
- };
-}
- </pre>
-
- <p>The <code>schema_version()</code> and <code>schema_migration()</code>
- accessors return the current database version and migration flag,
- respectively. The optional <code>name</code> argument is the schema
- name. If the database schema hasn't been created (that is, there is
- no corresponding entry in the <code>schema_version</code> table or
- this table does not exist), then <code>schema_version()</code> returns
- <code>0</code>. The <code>schema_version_migration()</code> accessor
- returns both version and migration flag together in the
- <code>schema_version_migration</code> <code>struct</code>.</p>
-
- <p>You may already have a version table in your database or you (or your
- database administrator) may prefer to keep track of versions your own
- way. You can instruct ODB not to create the <code>schema_version</code>
- table with the <code>--suppress-schema-version</code> option. However,
- ODB still needs to know the current database version in order for certain
- schema evolution mechanisms to function properly. As a result, in
- this case, you will need to set the schema version on the database
- instance manually using the schema_version_migration() modifier.
- Note that the modifier API is not thread-safe. That is, you should
- not modify the schema version while other threads may be accessing
- or modifying the same information.</p>
-
- <p>Note also that the accessors we discussed above will only query the
- <code>schema_version</code> table once and, if the version could
- be determined, cache the result. If, however, the version could
- not be determined (that is, <code>schema_version()</code> returned
- 0), then a subsequent call will re-query the table. While it is
- probably a bad idea to modify the database schema while the
- application is running (other than via the <code>schema_catalog</code>
- API, as discussed below), if for some reason you need ODB to re-query
- the version, then you can manually set it to 0 using the
- <code>schema_version_migration()</code> modifier.</p>
-
- <p>It is also possible to change the name of the table that stores
- the schema version using the <code>--schema-version-table</code>
- option. You will also need to specify this alternative name on
- the <code>database</code> instance using the <code>schema_version_table()</code>
- modifier. The first version specifies the default table that is
- used for all the schema names. The second version specifies the
- table for a specific schema. The table name should be
- database-quoted, if necessary.</p>
-
- <p>If we are generating our schema migrations as standalone SQL files,
- then the migration workflow could look like this:</p>
-
- <ol>
- <li>The database administrator determines the current database version.
- If migration is required, then for each migration step (that
- is, from one version to the next), they perform the following:</li>
-
- <li>Execute the pre-migration file.</li>
-
- <li>Execute our application (or a separate migration program)
- to perform data migration (discussed later). Our application
- can determine that is is being executed in the "migration mode"
- by calling <code>schema_migration()</code> and then which
- migration code to run by calling <code>schema_version()</code>.</li>
-
- <li>Execute the post-migration file.</li>
- </ol>
-
- <p>These steps become more integrated and automatic if we embed the
- schema creation and migration code into the generated C++ code.
- Now we can perform schema creation, schema migration, and data
- migration as well as determine when each step is necessary
- programmatically from within the application.</p>
-
- <p>Schema evolution support adds the following extra functions to
- the <code>odb::schema_catalog</code> class, which we first discussed
- in <a href="#3.4">Section 3.4, "Database"</a>.</p>
-
- <pre class="cxx">
-namespace odb
-{
- class schema_catalog
- {
- public:
- ...
-
-
- // Schema migration.
- //
- static void
- migrate_schema_pre (database&amp;,
- schema_version,
- const std::string&amp; name = "");
-
- static void
- migrate_schema_post (database&amp;,
- schema_version,
- const std::string&amp; name = "");
-
- static void
- migrate_schema (database&amp;,
- schema_version,
- const std::string&amp; name = "");
-
- // Data migration.
- //
- // Discussed in the next section.
-
-
- // Combined schema and data migration.
- //
- static void
- migrate (database&amp;,
- schema_version = 0,
- const std::string&amp; name = "");
-
- // Schema version information.
- //
- static schema_version
- base_version (const database&amp;,
- const std::string&amp; name = "");
-
- static schema_version
- base_version (database_id,
- const std::string&amp; name = "");
-
- static schema_version
- current_version (const database&amp;,
- const std::string&amp; name = "");
-
- static schema_version
- current_version (database_id,
- const std::string&amp; name = "");
-
- static schema_version
- next_version (const database&amp;,
- schema_version = 0,
- const std::string&amp; name = "");
-
- static schema_version
- next_version (database_id,
- schema_version,
- const std::string&amp; name = "");
- };
-}
- </pre>
-
- <p>The <code>migrate_schema_pre()</code> and
- <code>migrate_schema_post()</code> static functions perform
- a single stage (that is, pre or post) of a single migration
- step (that is, from one version to the next). The <code>version</code>
- argument specifies the version we are migrating to. For
- instance, in our <code>person</code> example, if we know that
- the database version is <code>1</code> and the next version
- is <code>2</code>, then we can execute code like this:</p>
-
- <pre class="cxx">
-transaction t (db.begin ());
-
-schema_catalog::migrate_schema_pre (db, 2);
-
-// Data migration goes here.
-
-schema_catalog::migrate_schema_post (db, 2);
-
-t.commit ();
- </pre>
-
- <p>If you don't have any data migration code to run, then you can
- perform both stages with a single call using the
- <code>migrate_schema()</code> static function.</p>
-
- <p>The <code>migrate()</code> static function perform both schema
- and data migration (we discuss data migration in the next section).
- It can also perform several migration steps at once. If we don't
- specify its target version, then it will migrate (if necessary)
- all the way to the current model version. As an extra convenience,
- <code>migrate()</code> will also create the database schema if
- none exists. As a result, if we don't have any data migration
- code or we have registered it with <code>schema_catalog</code> (as
- discussed later), then the database schema creation and migration,
- whichever is necessary, if at all, can be performed with a single
- function call:</p>
-
- <pre class="cxx">
-transaction t (db.begin ());
-schema_catalog::migrate (db);
-t.commit ();
- </pre>
-
- <p>Note also that <code>schema_catalog</code> is integrated with the
- <code>odb::database</code> schema version API. In particular,
- <code>schema_catalog</code> functions will query and synchronize
- the schema version on the <code>database</code> instance if and
- when required.</p>
-
- <p>The <code>schema_catalog</code> class also allows you to iterate
- over known versions (remember, there could be "gaps" in version
- numbers) with the <code>base_version()</code>,
- <code>current_version()</code> and <code>next_version()</code>
- static functions. The <code>base_version()</code> and
- <code>current_version()</code> functions return the base and
- current object model versions, respectively. That is, the
- lowest version from which we can migrate and the version that
- we ultimately want to migrate to. The <code>next_version()</code>
- function returns the next known version. If the passed version is
- greater or equal to the current version, then this function
- will return the current version plus one (that is, one past
- current). If we don't specify the version, then
- <code>next_version()</code> will use the current database version
- as the starting point. Note also that the schema version information
- provided by these functions is only available if we embed the schema
- migration code into the generated C++ code. For standalone SQL file
- migrations this information is normally not needed since the migration
- process is directed by an external entity, such as a database
- administrator or a script.</p>
-
- <p>Most <code>schema_catalog</code> functions presented above also accept
- the optional schema name argument. If the passed schema name is not
- found, then the <code>odb::unknown_schema</code> exception is
- thrown. Similarly, functions that accept the schema version argument will
- throw the <code>odb::unknown_schema_version</code> exception if the
- passed or current version is invalid. Refer to <a href="#3.14">Section
- 3.14, "ODB Exceptions"</a> for more information on these exceptions.</p>
-
- <p>To illustrate how all these parts fit together, consider the
- following more realistic database schema management example.
- Here we want to handle the schema creation in a special way
- and perform each migration step in its own transaction.</p>
-
- <pre class="cxx">
-schema_version v (db.schema_version ());
-schema_version bv (schema_catalog::base_version (db));
-schema_version cv (schema_catalog::current_version (db));
-
-if (v == 0)
-{
- // No schema in the database. Create the schema and
- // initialize the database.
- //
- transaction t (db.begin ());
- schema_catalog::create_schema (db);
-
- // Populate the database with initial data, if any.
-
- t.commit ();
-}
-else if (v &lt; cv)
-{
- // Old schema (and data) in the database, migrate them.
- //
-
- if (v &lt; bv)
- {
- // Error: migration from this version is no longer supported.
- }
-
- for (v = schema_catalog::next_version (db, v);
- v &lt;= cv;
- v = schema_catalog::next_version (db, v))
- {
- transaction t (db.begin ());
- schema_catalog::migrate_schema_pre (db, v);
-
- // Data migration goes here.
-
- schema_catalog::migrate_schema_post (db, v);
- t.commit ();
- }
-}
-else if (v > cv)
-{
- // Error: old application trying to access new database.
-}
- </pre>
-
- <h2><a name="13.3">13.3 Data Migration</a></h2>
-
- <p>In quite a few cases specifying the default value for new data
- members will be all that's required to handle the existing objects.
- For example, the natural default value for the new middle name
- that we have added is an empty string. And we can handle
- this case with the <code>db&nbsp;default</code> pragma and without
- any extra C++ code:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 2)
-
-#pragma db object
-class person
-{
- ...
-
-
- #pragma db default("")
- std::string middle_;
-};
- </pre>
-
- <p>However, there will be situations where we would need to perform
- more elaborate data migrations, that is, convert old data to the
- new format. As an example, suppose we want to add gender to our
- <code>person</code> class. And, instead of leaving it unassigned
- for all the existing objects, we will try to guess it from the
- first name. This is not particularly accurate but it could be
- sufficient for our hypothetical application:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 3)
-
-enum gender {male, female};
-
-#pragma db object
-class person
-{
- ...
-
- gender gender_;
-};
- </pre>
-
- <p>As we have discussed earlier, there are two ways to perform data
- migration: immediate and gradual. To recap, with immediate
- migration we migrate all the existing objects at once, normally
- after the schema pre-migration statements but before the
- post-migration statements. With gradual migration, we make sure
- the new object model can accommodate both old and new data and
- gradually migrate existing objects as the application runs and
- the opportunities to do so arise, for example, an object is
- updated.</p>
-
- <p>There is also another option for data migration that is not
- discussed further in this section. Instead of using our C++
- object model we could execute ad-hoc SQL statements that
- perform the necessary conversions and migrations directly
- on the database server. While in certain cases this can be
- a better option from the performance point of view, this
- approach is often limited in terms of the migration logic
- that we can handle.</p>
-
- <h2><a name="13.3.1">13.3.1 Immediate Data Migration</a></h2>
-
- <p>Let's first see how we can implement an immediate migration for the
- new <code>gender_</code> data member we have added above. If we
- are using standalone SQL files for migration, then we could add
- code along these lines somewhere early in <code>main()</code>,
- before the main application logic:</p>
-
- <pre class="cxx">
-int
-main ()
-{
- ...
-
- odb::database&amp; db = ...
-
- // Migrate data if necessary.
- //
- if (db.schema_migration ())
- {
- switch (db.schema_version ())
- {
- case 3:
- {
- // Assign gender to all the existing objects.
- //
- transaction t (db.begin ());
-
- for (person&amp; p: db.query&lt;person> ())
- {
- p.gender (guess_gender (p.first ()));
- db.update (p);
- }
-
- t.commit ();
- break;
- }
- }
- }
-
- ...
-}
- </pre>
-
- <p>If you have a large number of objects to migrate, it may also be
- a good idea, from the performance point of view, to break one big
- transaction that we now have into multiple smaller transactions
- (<a href="#3.5">Section 3.5, "Transactions"</a>). For example:</p>
-
- <pre class="cxx">
-case 3:
- {
- transaction t (db.begin ());
-
- size_t n (0);
- for (person&amp; p: db.query&lt;person> ())
- {
- p.gender (guess_gender (p.first ()));
- db.update (p);
-
- // Commit the current transaction and start a new one after
- // every 100 updates.
- //
- if (n++ % 100 == 0)
- {
- t.commit ();
- t.reset (db.begin ());
- }
- }
-
- t.commit ();
- break;
- }
- </pre>
-
- <p>While it looks straightforward enough, as we add more migration
- snippets, this approach can quickly become unmaintainable. Instead
- of having all the migrations in a single function and determining
- when to run each piece ourselves, we can package each migration into
- a separate function, register it with the <code>schema_catalog</code>
- class, and let ODB figure out when to run which migration functions.
- To support this functionality, <code>schema_catalog</code> provides
- the following data migration API:</p>
-
- <pre class="cxx">
-namespace odb
-{
- class schema_catalog
- {
- public:
- ...
-
- // Data migration.
- //
- static std::size_t
- migrate_data (database&amp;,
- schema_version = 0,
- const std::string&amp; name = "");
-
- typedef void data_migration_function_type (database&amp;);
-
- // Common (for all the databases) data migration, C++98/03 version:
- //
- template &lt;schema_version v, schema_version base>
- static void
- data_migration_function (data_migration_function_type*,
- const std::string&amp; name = "");
-
- // Common (for all the databases) data migration, C++11 version:
- //
- template &lt;schema_version v, schema_version base>
- static void
- data_migration_function (std::function&lt;data_migration_function_type>,
- const std::string&amp; name = "");
-
- // Database-specific data migration, C++98/03 version:
- //
- template &lt;schema_version v, schema_version base>
- static void
- data_migration_function (database&amp;,
- data_migration_function_type*,
- const std::string&amp; name = "");
-
- template &lt;schema_version v, schema_version base>
- static void
- data_migration_function (database_id,
- data_migration_function_type*,
- const std::string&amp; name = "");
-
- // Database-specific data migration, C++11 version:
- //
- template &lt;schema_version v, schema_version base>
- static void
- data_migration_function (database&amp;,
- std::function&lt;data_migration_function_type>,
- const std::string&amp; name = "");
-
- template &lt;schema_version v, schema_version base>
- static void
- data_migration_function (database_id,
- std::function&lt;data_migration_function_type>,
- const std::string&amp; name = "");
- };
-
- // Static data migration function registration, C++98/03 version:
- //
- template &lt;schema_version v, schema_version base>
- struct data_migration_entry
- {
- data_migration_entry (data_migration_function_type*,
- const std::string&amp; name = "");
-
- data_migration_entry (database_id,
- data_migration_function_type*,
- const std::string&amp; name = "");
- };
-
- // Static data migration function registration, C++11 version:
- //
- template &lt;schema_version v, schema_version base>
- struct data_migration_entry
- {
- data_migration_entry (std::function&lt;data_migration_function_type>,
- const std::string&amp; name = "");
-
- data_migration_entry (database_id,
- std::function&lt;data_migration_function_type>,
- const std::string&amp; name = "");
- };
-}
- </pre>
-
- <p>The <code>migrate_data()</code> static function performs data
- migration for the specified version. If no version is specified,
- then it will use the current database version and also check
- whether the database is in migration, that is,
- <code>database::schema_migration()</code> returns <code>true</code>.
- As a result, all we need to do in our <code>main()</code> is call
- this function. It will check if migration is required and if so,
- call all the migration functions registered for this version. For
- example:</p>
-
- <pre class="cxx">
-int
-main ()
-{
- ...
-
- database&amp; db = ...
-
- // Check if we need to migrate any data and do so
- // if that's the case.
- //
- schema_catalog::migrate_data (db);
-
- ...
-}
- </pre>
-
- <p>The <code>migrate_data()</code> function returns the number of
- migration functions called. You can use this value for debugging
- or logging.</p>
-
- <p>The only other step that we need to perform is register our data
- migration functions with <code>schema_catalog</code>. At the
- lower level we can call the <code>data_migration_function()</code>
- static function for every migration function we have, for example,
- at the beginning of <code>main()</code>. For each version, data
- migration functions are called in the order of registration.</p>
-
- <p>A more convenient approach, however, is to use the
- <code>data_migration_entry</code> helper class template to register the
- migration functions during static initialization. This way we
- can keep the migration function and its registration code next
- to each other. Here is how we can reimplement our <code>gender</code>
- migration code to use this mechanism:</p>
-
- <pre class="cxx">
-static void
-migrate_gender (odb::database&amp; db)
-{
- transaction t (db.begin ());
-
- for (person&amp; p: db.query&lt;person> ())
- {
- p.gender (guess_gender (p.first ()));
- db.update (p);
- }
-
- t.commit ();
-}
-
-static const odb::data_migration_entry&lt;3, MYAPP_BASE_VERSION>
-migrate_gender_entry (&amp;migrate_gender);
- </pre>
-
- <p>The first template argument to the <code>data_migration_entry</code>
- class template is the version we want this data migration function
- to be called for. The second template argument is the base model
- version. This second argument is necessary to detect the situation
- where we no longer need this data migration function. Remember
- that when we move the base model version forward, migrations from
- any version below the new base are no longer possible. We, however,
- may still have migration functions registered for those lower
- versions. Since these functions will never be called, they are
- effectively dead code and it would be useful to identify and
- remove them. To assist with this, <code>data_migration_entry</code>
- (and lower lever <code>data_migration_function()</code>) will
- check at compile time (that is, <code>static_assert</code>) that
- the registration version is greater than the base model version.</p>
-
- <p>In the above example we use the <code>MYAPP_BASE_VERSION</code>
- macro that is presumably defined in a central place, for example,
- <code>version.hxx</code>. This is the recommended approach since
- we can update the base version in a single place and have the
- C++ compiler automatically identify all the data migration
- functions that can be removed.</p>
-
- <p>In C++11 we can also create a template alias so that we don't
- have to repeat the base model macro in every registration, for
- example:</p>
-
- <pre class="cxx">
-template &lt;schema_version v>
-using migration_entry = odb::data_migration_entry&lt;v, MYAPP_BASE_VERSION>;
-
-static const migration_entry&lt;3>
-migrate_gender_entry (&amp;migrate_gender);
- </pre>
-
- <p>For cases where you need to by-pass the base version check, for
- example, to implement your own registration helper, ODB also
- provides "unsafe" versions of the <code>data_migration_function()</code>
- functions that take the version as a function argument rather than
- as a template parameter.</p>
-
- <p>In C++11 we can also use lambdas as migration functions, which makes
- the migration code more concise:</p>
-
- <pre class="cxx">
-static const migration_entry&lt;3>
-migrate_gender_entry (
- [] (odb::database&amp; db)
- {
- transaction t (db.begin ());
-
- for (person&amp; p: db.query&lt;person> ())
- {
- p.gender (guess_gender (p.first ()));
- db.update (p);
- }
-
- t.commit ();
- });
- </pre>
-
- <p>If we are using embedded schema migrations, then both schema and
- data migration is integrated and can be performed with a single
- call to the <code>schema_catalog::migrate()</code> function that
- we discussed earlier. For example:</p>
-
-<pre class="cxx">
-int
-main ()
-{
- ...
-
- database&amp; db = ...
-
- // Check if we need to migrate the database and do so
- // if that's the case.
- //
- {
- transaction t (db.begin ());
- schema_catalog::migrate (db);
- t.commit ();
- }
-
- ...
-}
- </pre>
-
- <p>Note, however, that in this case we call <code>migrate()</code>
- within a transaction (for the schema migration part) which means
- that our migration functions will also be called within this
- transaction. As a result, we will need to adjust our migration
- functions not to start their own transaction:</p>
-
- <pre class="cxx">
-static void
-migrate_gender (odb::database&amp; db)
-{
- // Assume we are already in a transaction.
- //
- for (person&amp; p: db.query&lt;person> ())
- {
- p.gender (guess_gender (p.first ()));
- db.update (p);
- }
-}
- </pre>
-
- <p>If, however, we want more granular transactions, then we can
- use the lower-level <code>schema_catalog</code> functions to
- gain more control, as we have seen at the end of the previous
- section. Here is the relevant part of that example with
- an added data migration call:</p>
-
- <pre class="cxx">
- // Old schema (and data) in the database, migrate them.
- //
- for (v = schema_catalog::next_version (db, v);
- v &lt;= cv;
- v = schema_catalog::next_version (db, v))
- {
- transaction t (db.begin ());
- schema_catalog::migrate_schema_pre (db, v);
- schema_catalog::migrate_data (db, v);
- schema_catalog::migrate_schema_post (db, v);
- t.commit ();
- }
- </pre>
-
- <h2><a name="13.3.2">13.3.2 Gradual Data Migration</a></h2>
-
- <p>If the number of existing objects that require migration is large,
- then an all-at-once, immediate migration, while simple, may not
- be practical from a performance point of view. In this case,
- we can perform a gradual migration as the application does
- its normal functions.</p>
-
- <p>With gradual migrations, the object model must be capable of
- representing data that conforms to both old and new formats at
- the same time since, in general, the database will contain a
- mixture of old and new objects. For example, in case of our
- <code>gender</code> data member, we need a special value that
- represents the "no gender assigned yet" case (an old object).
- We also need to assign this special value to all the existing
- objects during the schema pre-migration stage. One way to do
- this would be add a special value to our <code>gender</code>
- enum and then make it the default value with the
- <code>db&nbsp;default</code> pragma. A cleaner and easier approach,
- however, is to use <code>NULL</code> as a special value. We
- can add support for the <code>NULL</code> value semantics
- to any existing type by wrapping it with
- <code>odb::nullable</code>, <code>boost::optional</code>
- or similar (<a href="#7.3">Section 7.3, "Pointers and <code>NULL</code>
- Value Semantics"</a>). We also don't need to specify the default value
- explicitly since <code>NULL</code> is used automatically. Here
- is how we can use this approach in our <code>gender</code>
- example:</p>
-
- <pre class="cxx">
-#include &lt;odb/nullable.hxx>
-
-#pragma db object
-class person
-{
- ...
-
- odb::nullable&lt;gender> gender_;
-};
- </pre>
-
- <p>A variety of strategies can be employed to implement gradual
- migrations. For example, we can migrate the data when the object
- is updated as part of the normal application logic. While there
- is no migration cost associated with this approach (the object
- is updated anyway), depending on how often objects are typically
- updated, this strategy can take a long time to complete. An
- alternative strategy would be to perform an update whenever
- an old object is loaded. Yet another strategy is to have a
- separate thread that slowly migrates all the old objects as
- the application runs.</p>
-
- <p>As an example, let us implement the first approach for our
- <code>gender</code> migration. While we could have added
- the necessary code throughout the application, from the
- maintenance point of view, it is best to try and localize
- the gradual migration logic to the persistent classes that
- it affects. And for this database operation callbacks
- (<a href="#14.1.7">Section 14.1.7, "<code>callback</code>"</a>)
- are a very useful mechanism. In our case, all we have to do is handle
- the <code>post_load</code> event where we guess the gender
- if it is <code>NULL</code>:</p>
-
- <pre class="cxx">
-#include &lt;odb/core.hxx> // odb::database
-#include &lt;odb/callback.hxx> // odb::callback_event
-#include &lt;odb/nullable.hxx>
-
-#pragma db object callback(migrate)
-class person
-{
- ...
-
- void
- migrate (odb::callback_event e, odb::database&amp;)
- {
- if (e == odb::callback_event::post_load)
- {
- // Guess gender if not assigned.
- //
- if (gender_.null ())
- gender_ = guess_gender (first_);
- }
- }
-
- odb::nullable&lt;gender> gender_;
-};
- </pre>
-
- <p>In particular, we don't have to touch any of the accessors
- or modifiers or the application logic &mdash; all of them
- can assume that the value can never be <code>NULL</code>.
- And when the object is next updated, the new <code>gender</code>
- value will be stored automatically.</p>
-
- <p>All gradual migrations normally end up with a terminating
- immediate migration some number of versions down the line,
- when the bulk of the objects has presumably been converted.
- This way we don't have to keep the gradual migration code
- around forever. Here is how we could implement a terminating
- migration for our example:</p>
-
- <pre class="cxx">
-// person.hxx
-//
-#pragma db model version(1, 4)
-
-#pragma db object
-class person
-{
- ...
-
- gender gender_;
-};
-
-// person.cxx
-//
-static void
-migrate_gender (odb::database&amp; db)
-{
- typedef odb::query&lt;person> query;
-
- for (person&amp; p: db.query&lt;person> (query::gender.is_null ()))
- {
- p.gender (guess_gender (p.first ()));
- db.update (p);
- }
-}
-
-static const odb::data_migration_entry&lt;4, MYAPP_BASE_VERSION>
-migrate_gender_entry (&amp;migrate_gender);
- </pre>
-
- <p>A couple of points to note about this code. Firstly, we
- removed all the gradual migration logic (the callback)
- from the class and replaced it with the immediate migration
- function. We also removed the <code>odb::nullable</code>
- wrapper (and therefore disallowed the <code>NULL</code> values)
- since after this migration all the objects will have been
- converted. Finally, in the migration function, we only query
- the database for objects that need migration, that is, have
- <code>NULL</code> gender.</p>
-
- <h2><a name="13.4">13.4 Soft Object Model Changes</a></h2>
-
- <p>Let us consider another common kind of object model change:
- we delete an old member, add a new one, and need to copy
- the data from the old to the new, perhaps applying some
- conversion. For example, we may realize that in our application
- it is a better idea to store a person's name as a single string
- rather than split it into three fields. So what we would like to do
- is add a new data member, let's call it <code>name_</code>, convert
- all the existing split names, and then delete the <code>first_</code>,
- <code>middle_</code>, and <code>last_</code> data members.</p>
-
- <p>While this sounds straightforward, there is a problem. If we
- delete (that is, physically remove from the source code) the
- old data members, then we won't be able to access the old
- data. The data will still be available in the database between
- the schema pre and post-migrations, it is just we will no longer
- be able to access it through our object model. And if we keep
- the old data members around, then the old data will remain
- stored in the database even after the schema post-migration.</p>
-
- <p>There is also a more subtle problem that has to do with existing
- migrations for the previous versions. Remember, in version <code>3</code>
- of our <code>person</code> example we added the <code>gender_</code>
- data member. We also have a data migration function which guesses
- the gender based on the first name. Deleting the <code>first_</code>
- data member from our class will obviously break this code. But
- even adding the new <code>name_</code> data member will cause
- problems because when we try to update the object in order to
- store the new gender, ODB will try to update <code>name_</code>
- as well. But there is no corresponding column in the database
- yet. When we run this migration function, we are still several
- versions away from the point where the <code>name</code> column
- will be added.</p>
-
- <p>This is a very subtle but also very important implication to
- understand. Unlike the main application logic, which only needs
- to deal with the current model version, data migration code works
- on databases that can be multiple versions behind the current
- version.</p>
-
- <p>How can we resolve this problem? It appears what we need is the
- ability to add or delete data members starting from a specific
- version. In ODB this mechanism is called soft member additions
- and deletions. A soft-added member is only treated as persistent
- starting from the addition version. A soft-deleted member is
- persistent until the deletion version (but including the migration
- stage). In its essence, soft model changes allow us to maintain
- multiple versions of our object model all with a single set of
- persistent classes. Let us now see how this functionality can
- help implement our changes:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 4)
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-
- #pragma db deleted(4)
- std::string first_;
-
- #pragma db deleted(4)
- std::string middle_;
-
- #pragma db deleted(4)
- std::string last_;
-
- #pragma db added(4)
- std::string name_;
-
- gender gender_;
-};
- </pre>
-
- <p>The migration function for this change could then look like
- this:</p>
-
- <pre class="cxx">
-static void
-migrate_name (odb::database&amp; db)
-{
- for (person&amp; p: db.query&lt;person> ())
- {
- p.name (p.first () + " " +
- p.middle () + (p.middle ().empty () ? "" : " ") +
- p.last ());
- db.update (p);
- }
-}
-
-static const odb::data_migration_entry&lt;4, MYAPP_BASE_VERSION>
-migrate_name_entry (&amp;migrate_name);
- </pre>
-
- <p>Note also that no changes are required to the gender migration
- function.</p>
-
- <p>As you may have noticed, in the code above we assumed that the
- <code>person</code> class still provides public accessors for
- the now deleted data members. This might not be ideal since now
- they should not be used by the application logic. The only code
- that may still need to access them is the migration functions. The
- recommended way to resolve this is to remove the accessors/modifiers
- corresponding to the deleted data member, make migration functions
- static functions of the class being migrated, and then access
- the deleted data members directly. For example:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 4)
-
-#pragma db object
-class person
-{
- ...
-
-private:
- friend class odb::access;
-
- #pragma db id auto
- unsigned long id_;
-
- #pragma db deleted(4)
- std::string first_;
-
- #pragma db deleted(4)
- std::string middle_;
-
- #pragma db deleted(4)
- std::string last_;
-
- #pragma db added(4)
- std::string name_;
-
- gender gender_;
-
-private:
- static void
- migrate_gender (odb::database&amp;);
-
- static void
- migrate_name (odb::database&amp;);
-};
-
-void person::
-migrate_gender (odb::database&amp; db)
-{
- for (person&amp; p: db.query&lt;person> ())
- {
- p.gender_ = guess_gender (p.first_);
- db.update (p);
- }
-}
-
-static const odb::data_migration_entry&lt;3, MYAPP_BASE_VERSION>
-migrate_name_entry (&amp;migrate_gender);
-
-void person::
-migrate_name (odb::database&amp; db)
-{
- for (person&amp; p: db.query&lt;person> ())
- {
- p.name_ = p.first_ + " " +
- p.middle_ + (p.middle_.empty () ? "" : " ") +
- p.last_;
- db.update (p);
- }
-}
-
-static const odb::data_migration_entry&lt;4, MYAPP_BASE_VERSION>
-migrate_name_entry (&amp;migrate_name);
- </pre>
-
- <p>Another potential issue with the soft-deletion is the requirement
- to keep the delete data members in the class. While they will not
- be initialized in the normal operation of the application (that
- is, not a migration), this can still be a problem if we need to
- minimize the memory footprint of our classes. For example, we may
- cache a large number of objects in memory and having three
- <code>std::string</code> data members can be a significant
- overhead.</p>
-
- <p>The recommended way to resolve this issue is to place all the
- deleted data members into a dynamically allocated composite
- value type. For example:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 4)
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-
- #pragma db added(4)
- std::string name_;
-
- gender gender_;
-
- #pragma db value
- struct deleted_data
- {
- #pragma db deleted(4)
- std::string first_;
-
- #pragma db deleted(4)
- std::string middle_;
-
- #pragma db deleted(4)
- std::string last_;
- };
-
- #pragma db column("")
- std::unique_ptr&lt;deleted_data> dd_;
-
- ...
-};
- </pre>
-
- <p>ODB will then automatically allocate the deleted value type if
- any of the deleted data members are being loaded. During the normal
- operation, however, the pointer will stay <code>NULL</code> and
- therefore reduce the common case overhead to a single pointer
- per class. Note that we make the composite value column prefix
- empty (the <code>db&nbsp;column("")</code> pragma) in order to
- keep the same column names for the deleted data members.</p>
-
- <p>Soft-added and deleted data members can be used in objects,
- composite values, views, and container value types. We can
- also soft-add and delete data members of simple, composite,
- pointer to object, and container types. Only special data
- members, such as the object id and the optimistic concurrency
- version, cannot be soft-added or deleted.</p>
-
- <p>It is also possible to soft-delete a persistent class. We
- can still work with the existing objects of such a class,
- however, no table is created in new databases for soft-deleted
- classes. To put it another way, a soft-delete class is like an
- abstract class (no table) but which can still be loaded, updated,
- etc. Soft-added persistent classes do not make much sense and
- are therefore not supported.</p>
-
- <p>As an example of a soft-deleted class, suppose we want to
- replace our <code>person</code> class with the new
- <code>employee</code> object and migrate the data. Here is
- how we could do this:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 5)
-
-#pragma db object deleted(5)
-class person
-{
- ...
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-
- std::string name_;
- gender gender_;
-
- static void
- migrate_person (odb::database&amp;);
-};
-
-void employee::
-migrate_person (odb::database&amp; db)
-{
- for (person&amp; p: db.query&lt;person> ())
- {
- employee e (p.name (), p.gender ());
- db.persist (e);
- }
-}
-
-static const odb::data_migration_entry&lt;5, MYAPP_BASE_VERSION>
-migrate_person_entry (&amp;migrate_person);
- </pre>
-
- <p>As we have seen above, hard member additions and deletions can
- (and most likely will) break existing data migration code. Why,
- then, not treat all the changes, or at least additions, as soft?
- ODB requires you to explicitly request this semantics because
- support for soft-added and deleted data members incurs runtime
- overhead. And there can be plenty of cases where there is no
- existing data migration and therefore hard additions and deletions
- are sufficient.</p>
-
- <p>In some cases a hard addition or deletion will result in a
- compile-time error. For example, one of the data migration
- functions may reference the data member we just deleted. In
- many cases, however, such errors can only be detected at
- runtime, and, worse yet, only when the migration function
- is executed. For example, we may hard-add a new data member
- that an existing migration function will try to indirectly
- store in the database as part of an object update. As a result,
- it is highly recommended that you always test your application
- with the database that starts at the base version so that every
- data migration function is called and therefore ensured to
- still work correctly.</p>
-
- <p>To help with this problem you can also instruct ODB to warn
- you about any hard additions or deletions with the
- <code>--warn-hard-add</code>, <code>--warn-hard-delete</code>,
- and <code>--warn-hard</code> command line options. ODB will
- only warn you about hard changes in the current version and
- only for as long as it is open, which makes this mechanism
- fairly usable.</p>
-
- <p>You may also be wondering why we have to specify the addition
- and deletion versions explicitly. It may seem like the ODB compiler
- should be able to figure this out automatically. While it is
- theoretically possible, to achieve this, ODB would have to also
- maintain a separate changelog of the C++ object model in
- addition to the database schema changelog it already maintains.
- While being a lot more complex, such an additional changelog
- would also complicate the workflow significantly. In this light,
- maintaining this change information as part of the original
- source files appears to be a cleaner and simpler approach.</p>
-
- <p>As we discussed before, when we move the base model version
- forward we essentially drop support for migrations from
- versions before the new base. As a result, it is no longer
- necessary to maintain the soft semantics of additions and
- deletions up to and including the new base version. ODB
- will issue diagnostics for all such members and classes.
- For soft deletions we can simply remove the data member or
- class entirely. For soft additions we only need to remove the
- <code>db&nbsp;added</code> pragma.</p>
-
- <h2><a name="13.4.1">13.4.1 Reuse Inheritance Changes</a></h2>
-
- <p>Besides adding and deleting data members, another way to alter
- the object's table is using reuse-style inheritance. If we add
- a new reuse base, then, from the database schema point of view,
- this is equivalent to adding all its columns to the derived
- object's table. Similarly, deleting reuse inheritance results in
- all the base's columns being deleted from the derived's table.</p>
-
- <p>In the future ODB may provide direct support for soft addition
- and deletion of inheritance. Currently, however, this semantics
- can be emulated with soft-added and deleted data members. The
- following table describes the most common scenarios depending
- on where columns are added or deleted, that is, base table,
- derived table, or both.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table class="scenarios" border="1">
- <tr>
- <th>DELETE</th>
- <th style="width: 40%">HARD</th>
- <th style="width: 40%">SOFT</th>
- </tr>
-
- <tr>
- <td>In both (delete inheritance and base)</td>
- <td>Delete inheritance and base. Move object id to derived.</td>
- <td>Soft-delete base. Mark all data members (except id) in
- base as soft-deleted.</td>
- </tr>
-
- <tr>
- <td>In base only (delete base)</td>
- <td>Option 1: mark base as abstract.<br/><br/>
- Option 2: move all the base member to derived, delete base.</td>
- <td>Soft-delete base.</td>
- </tr>
-
- <tr>
- <td>In derived only (delete inheritance)</td>
- <td>Delete inheritance, add object id to derived.</td>
- <td>Option 1: copy base to a new soft-deleted base, inherit
- from it instead. Mark all the data members (expect id) in
- this new base as soft-deleted. Note: we add the new base
- as soft-deleted to get notified when we can remove it.<br/><br/>
- Option 2: Copy all the data members from base to derived
- and mark them as soft-deleted in derived.</td>
- </tr>
- </table>
-
-
- <table class="scenarios" border="1">
- <tr>
- <th>ADD</th>
- <th style="width: 40%">HARD</th>
- <th style="width: 40%">SOFT</th>
- </tr>
-
- <tr>
- <td>In both (add new base and inheritance)</td>
- <td>Add new base and inheritance. Potentially move object id
- member from derived to base.</td>
- <td>Add new base and mark all its data members as soft-added.
- Add inheritance. Move object id from derived to base.</td>
- </tr>
-
- <tr>
- <td>In base only (refactor existing data to new base)</td>
- <td>Add new base and move data members from derived to base.
- Note: in most cases the new base will be made abstract
- which make this scenario non-schema changing.</td>
- <td>The same as HARD.</td>
- </tr>
-
- <tr>
- <td>In derived only (add inheritance to existing base)</td>
- <td>Add inheritance, delete object id in derived.</td>
- <td>Copy existing base to a new abstract base and inherit
- from it. Mark all the database members in the new base
- as soft-added (except object id). When notified by the
- ODB compiler that the soft addition of the data members
- is no longer necessary, delete the copy and inherit from
- the original base.</td>
- </tr>
- </table>
-
- <h2><a name="13.4.2">13.4.2 Polymorphism Inheritance Changes</a></h2>
-
- <p>Unlike reuse inheritance, adding or deleting a polymorphic base
- does not result in the base's data members being added or deleted
- from the derived object's table because each class in a polymorphic
- hierarchy is stored in a separate table. There are, however, other
- complications due to the presence of special columns (discriminator
- in the root table and object id links in derived tables) which makes
- altering the hierarchy structure difficult to handle automatically.
- Adding or deleting (including soft-deleting) of leaf classes (or
- leaf sub-hierarchies) in a polymorphic hierarchy is fully supported.
- Any more complex changes, such as adding or deleting the root or
- an intermediate base or getting an existing class into or out of
- a polymorphic hierarchy can be handled by creating a new leaf class
- (or leaf sub-hierarchy), soft-deleting the old class, and migrating
- the data.</p>
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="14">14 ODB Pragma Language</a></h1>
-
- <p>As we have already seen in previous chapters, ODB uses a pragma-based
- language to capture database-specific information about C++ types.
- This chapter describes the ODB pragma language in more detail. It
- can be read together with other chapters in the manual to get a
- sense of what kind of configurations and mapping fine-tuning are
- possible. You can also use this chapter as a reference at a later
- stage.</p>
-
- <p>An ODB pragma has the following syntax:</p>
-
- <p><code>#pragma db <i>qualifier</i> [<i>specifier</i> <i>specifier</i> ...]</code></p>
-
- <p>The <em>qualifier</em> tells the ODB compiler what kind of C++ construct
- this pragma describes. Valid qualifiers are <code>object</code>,
- <code>view</code>, <code>value</code>, <code>member</code>,
- <code>namespace</code>, <code>model</code>, <code>index</code>, and
- <code>map</code>.
- A pragma with the <code>object</code> qualifier describes a persistent
- object type. It tells the ODB compiler that the C++ class it describes
- is a persistent class. Similarly, pragmas with the <code>view</code>
- qualifier describe view types, the <code>value</code> qualifier
- describes value types and the <code>member</code> qualifier is used
- to describe data members of persistent object, view, and value types.
- The <code>namespace</code> qualifier is used to describe common
- properties of objects, views, and value types that belong to
- a C++ namespace while the <code>model</code> qualifier describes
- the whole C++ object model. The <code>index</code> qualifier defines
- a database index. And, finally, the <code>map</code> qualifier
- describes a mapping between additional database types and types
- for which ODB provides built-in support.</p>
-
- <p>The <em>specifier</em> informs the ODB compiler about a particular
- database-related property of the C++ declaration. For example, the
- <code>id</code> member specifier tells the ODB compiler that this
- member contains this object's identifier. Below is the declaration
- of the <code>person</code> class that shows how we can use ODB
- pragmas:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- #pragma db member id
- unsigned long id_;
- ...
-};
- </pre>
-
- <p>In the above example we don't explicitly specify which C++ class or
- data member the pragma belongs to. Rather, the pragma applies to
- a C++ declaration that immediately follows the pragma. Such pragmas
- are called <em>positioned pragmas</em>. In positioned pragmas that
- apply to data members, the <code>member</code> qualifier can be
- omitted for brevity, for example:</p>
-
- <pre class="cxx">
- #pragma db id
- unsigned long id_;
- </pre>
-
- <p>Note also that if the C++ declaration immediately following a
- position pragma is incompatible with the pragma qualifier, an
- error will be issued. For example:</p>
-
- <pre class="cxx">
- #pragma db object // Error: expected class instead of data member.
- unsigned long id_;
- </pre>
-
- <p>While keeping the C++ declarations and database declarations close
- together eases maintenance and increases readability, we can also
- place them in different parts of the same header file or even
- factor them to a separate file. To achieve this we use the so called
- <em>named pragmas</em>. Unlike positioned pragmas, named pragmas
- explicitly specify the C++ declaration to which they apply by
- adding the declaration name after the pragma qualifier. For example:</p>
-
- <pre class="cxx">
-class person
-{
- ...
-private:
- unsigned long id_;
- ...
-};
-
-#pragma db object(person)
-#pragma db member(person::id_) id
- </pre>
-
- <p>Note that in the named pragmas for data members the <code>member</code>
- qualifier is no longer optional. The C++ declaration name in the
- named pragmas is resolved using the standard C++ name resolution
- rules, for example:</p>
-
- <pre class="cxx">
-namespace db
-{
- class person
- {
- ...
- private:
- unsigned long id_;
- ...
- };
-}
-
-namespace db
-{
- #pragma db object(person) // Resolves db::person.
-}
-
-#pragma db member(db::person::id_) id
- </pre>
-
- <p>As another example, the following code fragment shows how to use the
- named value type pragma to map a C++ type to a native database type:</p>
-
- <pre class="cxx">
-#pragma db value(bool) type("INT")
-
-#pragma db object
-class person
-{
- ...
-private:
- bool married_; // Mapped to INT NOT NULL database type.
- ...
-};
- </pre>
-
- <p>If we would like to factor the ODB pragmas into a separate file,
- we can include this file into the original header file (the one
- that defines the persistent types) using the <code>#include</code>
- directive, for example:</p>
-
- <pre class="cxx">
-// person.hxx
-
-class person
-{
- ...
-};
-
-#ifdef ODB_COMPILER
-# include "person-pragmas.hxx"
-#endif
- </pre>
-
- <p>Alternatively, instead of using the <code>#include</code> directive,
- we can use the <code>--odb-epilogue</code> option to make the pragmas
- known to the ODB compiler when compiling the original header file,
- for example:</p>
-
- <pre class="terminal">
---odb-epilogue '#include "person-pragmas.hxx"'
- </pre>
-
- <p>The following sections cover the specifiers applicable to all the
- qualifiers mentioned above.</p>
-
- <p>The C++ header file that defines our persistent classes and
- normally contains one or more ODB pragmas is compiled by both
- the ODB compiler to generate the database support code and
- the C++ compiler to build the application. Some C++ compilers
- issue warnings about pragmas that they do not recognize. There
- are several ways to deal with this problem which are covered
- at the end of this chapter in <a href="#14.9">Section 14.9,
- "C++ Compiler Warnings"</a>.</p>
-
- <h2><a name="14.1">14.1 Object Type Pragmas</a></h2>
-
- <p>A pragma with the <code>object</code> qualifier declares a C++ class
- as a persistent object type. The qualifier can be optionally followed,
- in any order, by one or more specifiers summarized in the table below:</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table class="specifiers" border="1">
- <tr>
- <th>Specifier</th>
- <th>Summary</th>
- <th>Section</th>
- </tr>
-
- <tr>
- <td><code>table</code></td>
- <td>table name for a persistent class</td>
- <td><a href="#14.1.1">14.1.1</a></td>
- </tr>
-
- <tr>
- <td><code>pointer</code></td>
- <td>pointer type for a persistent class</td>
- <td><a href="#14.1.2">14.1.2</a></td>
- </tr>
-
- <tr>
- <td><code>abstract</code></td>
- <td>persistent class is abstract</td>
- <td><a href="#14.1.3">14.1.3</a></td>
- </tr>
-
- <tr>
- <td><code>readonly</code></td>
- <td>persistent class is read-only</td>
- <td><a href="#14.1.4">14.1.4</a></td>
- </tr>
-
- <tr>
- <td><code>optimistic</code></td>
- <td>persistent class with the optimistic concurrency model</td>
- <td><a href="#14.1.5">14.1.5</a></td>
- </tr>
-
- <tr>
- <td><code>no_id</code></td>
- <td>persistent class has no object id</td>
- <td><a href="#14.1.6">14.1.6</a></td>
- </tr>
-
- <tr>
- <td><code>callback</code></td>
- <td>database operations callback</td>
- <td><a href="#14.1.7">14.1.7</a></td>
- </tr>
-
- <tr>
- <td><code>schema</code></td>
- <td>database schema for a persistent class</td>
- <td><a href="#14.1.8">14.1.8</a></td>
- </tr>
-
- <tr>
- <td><code>polymorphic</code></td>
- <td>persistent class is polymorphic</td>
- <td><a href="#14.1.9">14.1.9</a></td>
- </tr>
-
- <tr>
- <td><code>session</code></td>
- <td>enable/disable session support for a persistent class</td>
- <td><a href="#14.1.10">14.1.10</a></td>
- </tr>
-
- <tr>
- <td><code>definition</code></td>
- <td>definition location for a persistent class</td>
- <td><a href="#14.1.11">14.1.11</a></td>
- </tr>
-
- <tr>
- <td><code>transient</code></td>
- <td>all non-virtual data members in a persistent class are transient</td>
- <td><a href="#14.1.12">14.1.12</a></td>
- </tr>
-
- <tr>
- <td><code>sectionable</code></td>
- <td>support addition of new sections in derived classes</td>
- <td><a href="#14.1.13">14.1.13</a></td>
- </tr>
-
- <tr>
- <td><code>deleted</code></td>
- <td>persistent class is soft-deleted</td>
- <td><a href="#14.1.14">14.1.14</a></td>
- </tr>
-
- <tr>
- <td><code>bulk</code></td>
- <td>enable bulk operations for a persistent class</td>
- <td><a href="#14.1.15">14.1.15</a></td>
- </tr>
-
- </table>
-
- <h3><a name="14.1.1">14.1.1 <code>table</code></a></h3>
-
- <p>The <code>table</code> specifier specifies the table name that should
- be used to store objects of the persistent class in a relational
- database. For example:</p>
-
- <pre class="cxx">
-#pragma db object table("people")
-class person
-{
- ...
-};
- </pre>
-
- <p>If the table name is not specified, the class name is used as the
- table name. The table name can be qualified with a database
- schema, for example:</p>
-
- <pre class="cxx">
-#pragma db object table("census.people")
-class person
-{
- ...
-};
- </pre>
-
- <p>For more information on database schemas and the format of the
- qualified names, refer to <a href="#14.1.8">Section 14.1.8,
- "<code>schema</code>"</a>.</p>
-
- <h3><a name="14.1.2">14.1.2 <code>pointer</code></a></h3>
-
- <p>The <code>pointer</code> specifier specifies the object pointer type
- for the persistent class. The object pointer type is used to return,
- pass, and cache dynamically allocated instances of a persistent
- class. For example:</p>
-
- <pre class="cxx">
-#pragma db object pointer(std::tr1::shared_ptr&lt;person>)
-class person
-{
- ...
-};
- </pre>
-
- <p>There are several ways to specify an object pointer with the
- <code>pointer</code> specifier. We can use a complete pointer
- type as shown in the example above. Alternatively, we can
- specify only the template name of a smart pointer in which
- case the ODB compiler will automatically append the class
- name as a template argument. The following example is therefore
- equivalent to the one above:</p>
-
- <pre class="cxx">
-#pragma db object pointer(std::tr1::shared_ptr)
-class person
-{
- ...
-};
- </pre>
-
- <p>If you would like to use the raw pointer as an object pointer,
- you can use <code>*</code> as a shortcut:</p>
-
- <pre class="cxx">
-#pragma db object pointer(*) // Same as pointer(person*)
-class person
-{
- ...
-};
- </pre>
-
- <p>If a pointer type is not explicitly specified, the default pointer,
- specified at the namespace level (<a href="#14.5.1">Section 14.5.1,
- "<code>pointer</code>"</a>) or with the <code>--default-pointer</code>
- ODB compiler option, is used. If neither of these two mechanisms is
- used to specify the pointer, then the raw pointer is used by default.</p>
-
- <p>For a more detailed discussion of object pointers, refer to
- <a href="#3.3">Section 3.3, "Object and View Pointers"</a>.</p>
-
- <h3><a name="14.1.3">14.1.3 <code>abstract</code></a></h3>
-
- <p>The <code>abstract</code> specifier specifies that the persistent class
- is abstract. An instance of an abstract class cannot be stored in
- the database and is normally used as a base for other persistent
- classes. For example:</p>
-
- <pre class="cxx">
-#pragma db object abstract
-class person
-{
- ...
-};
-
-#pragma db object
-class employee: public person
-{
- ...
-};
-
-#pragma db object
-class contractor: public person
-{
- ...
-};
- </pre>
-
- <p>Persistent classes with pure virtual functions are automatically
- treated as abstract by the ODB compiler. For a more detailed
- discussion of persistent class inheritance, refer to
- <a href="#8">Chapter 8, "Inheritance"</a>.</p>
-
- <h3><a name="14.1.4">14.1.4 <code>readonly</code></a></h3>
-
- <p>The <code>readonly</code> specifier specifies that the persistent class
- is read-only. The database state of read-only objects cannot be
- updated. In particular, this means that you cannot call the
- <code>database::update()</code> function (<a href="#3.10">Section 3.10,
- "Updating Persistent Objects"</a>) for such objects. For example:</p>
-
- <pre class="cxx">
-#pragma db object readonly
-class person
-{
- ...
-};
- </pre>
-
- <p>Read-only and read-write objects can derive from each other without
- any restrictions. When a read-only object derives from a read-write
- object, the resulting whole object is read-only, including the part
- corresponding to the read-write base. On the other hand, when a
- read-write object derives from a read-only object, all the data
- members that correspond to the read-only base are treated as
- read-only while the rest is treated as read-write.</p>
-
- <p>Note that it is also possible to declare individual data members
- (<a href="#14.4.12">Section 14.4.12, "<code>readonly</code>"</a>)
- as well as composite value types (<a href="#14.3.6">Section 14.3.6,
- "<code>readonly</code>"</a>) as read-only.</p>
-
- <h3><a name="14.1.5">14.1.5 <code>optimistic</code></a></h3>
-
- <p>The <code>optimistic</code> specifier specifies that the persistent class
- has the optimistic concurrency model. A class with the optimistic
- concurrency model must also specify the data member that is used to
- store the object version using the <code>version</code> pragma
- (<a href="#14.4.16">Section 14.4.16, "<code>version</code>"</a>).
- For example:</p>
-
- <pre class="cxx">
-#pragma db object optimistic
-class person
-{
- ...
-
- #pragma db version
- unsigned long version_;
-};
- </pre>
-
- <p>If a base class has the optimistic concurrency model, then all its derived
- classes will automatically have the optimistic concurrency model. The
- current implementation also requires that in any given inheritance
- hierarchy the object id and the version data members reside in the
- same class.</p>
-
- <p>For a more detailed discussion of optimistic concurrency, refer to
- <a href="#12">Chapter 12, "Optimistic Concurrency"</a>.</p>
-
- <h3><a name="14.1.6">14.1.6 <code>no_id</code></a></h3>
-
- <p>The <code>no_id</code> specifier specifies that the persistent class
- has no object id. For example:</p>
-
- <pre class="cxx">
-#pragma db object no_id
-class person
-{
- ...
-};
- </pre>
-
- <p>A persistent class without an object id has limited functionality.
- Such a class cannot be loaded with the <code>database::load()</code>
- or <code>database::find()</code> functions (<a href="#3.9">Section 3.9,
- "Loading Persistent Objects"</a>), updated with the
- <code>database::update()</code> function (<a href="#3.10">Section 3.10,
- "Updating Persistent Objects"</a>), or deleted with the
- <code>database::erase()</code> function (<a href="#3.11">Section 3.11,
- "Deleting Persistent Objects"</a>). To load and delete
- objects without ids you can use the <code>database::query()</code>
- (<a href="#4">Chapter 4, "Querying the Database"</a>) and
- <code>database::erase_query()</code> (<a href="#3.11">Section 3.11,
- "Deleting Persistent Objects"</a>) functions, respectively.
- There is no way to update such objects except by using native SQL
- statements (<a href="#3.12">Section 3.12, "Executing Native SQL
- Statements"</a>).</p>
-
- <p>Furthermore, persistent classes without object ids cannot have container
- data members nor can they be used in object relationships. Such objects
- are not entered into the session object cache
- (<a href="#11.1">Section 11.1, "Object Cache"</a>) either.</p>
-
- <p>To declare a persistent class with an object id, use the data member
- <code>id</code> specifier (<a href="#14.4.1">Section 14.4.1,
- "<code>id</code>"</a>).</p>
-
- <h3><a name="14.1.7">14.1.7 <code>callback</code></a></h3>
-
- <p>The <code>callback</code> specifier specifies the persist class
- member function that should be called before and after a
- database operation is performed on an object of this class.
- For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/callback.hxx>
-
-#pragma db object callback(init)
-class person
-{
- ...
-
- void
- init (odb::callback_event, odb::database&amp;);
-};
- </pre>
-
- <p>The callback function has the following signature and can be
- overloaded for constant objects:</p>
-
- <pre class="cxx">
-void
-name (odb::callback_event, odb::database&amp;);
-
-void
-name (odb::callback_event, odb::database&amp;) const;
- </pre>
-
- <p>The first argument to the callback function is the event that
- triggered this call. The <code>odb::callback_event</code>
- enum-like type is defined in the <code>&lt;odb/callback.hxx></code>
- header file and has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- struct callback_event
- {
- enum value
- {
- pre_persist,
- post_persist,
- pre_load,
- post_load,
- pre_update,
- post_update,
- pre_erase,
- post_erase
- };
-
- callback_event (value v);
- operator value () const;
- };
-}
- </pre>
-
- <p>The second argument to the callback function is the database
- on which the operation is about to be performed or has just
- been performed. A callback function can be inline or virtual.</p>
-
- <p>The callback function for the <code>*_persist</code>,
- <code>*_update</code>, and <code>*_erase</code> events is always
- called on the constant object reference while for the <code>*_load</code>
- events &mdash; always on the unrestricted reference.</p>
-
- <p>If only the non-<code>const</code> version of the callback function
- is provided, then only the <code>*_load</code> events will be delivered.
- If only the <code>const</code> version is provided, then all the
- events will be delivered to this function. Finally, if both versions
- are provided, then the <code>*_load</code> events will be delivered
- to the non-<code>const</code> version while all others &mdash; to the
- <code>const</code> version. If you need to modify the object in one
- of the "<code>const</code>" events, then you can safely cast away
- <code>const</code>-ness using the <code>const_cast</code> operator if
- you know that none of the objects will be created const. Alternatively,
- if you cannot make this assumption, then you can declare the data
- members you wish to modify as <code>mutable</code>.</p>
-
- <p>A database operations callback can be used to implement object-specific
- pre and post initializations, registrations, and cleanups. As an example,
- the following code fragment outlines an implementation of a
- <code>person</code> class that maintains the transient <code>age</code>
- data member in addition to the persistent date of birth. A callback
- is used to calculate the value of the former from the latter every
- time a <code>person</code> object is loaded from the database.</p>
-
- <pre class="cxx">
-#include &lt;odb/core.hxx>
-#include &lt;odb/callback.hxx>
-
-#pragma db object callback(init)
-class person
-{
- ...
-
-private:
- friend class odb::access;
-
- date born_;
-
- #pragma db transient
- unsigned short age_;
-
- void
- init (odb::callback_event e, odb::database&amp;)
- {
- switch (e)
- {
- case odb::callback_event::post_load:
- {
- // Calculate age from the date of birth.
- ...
- break;
- }
- default:
- break;
- }
- }
-};
- </pre>
-
- <h3><a name="14.1.8">14.1.8 <code>schema</code></a></h3>
-
- <p>The <code>schema</code> specifier specifies a database schema
- that should be used for the persistent class.</p>
-
- <p>In relational databases the term schema can refer to two related
- but ultimately different concepts. Normally it means a collection
- of tables, indexes, sequences, etc., that are created in the
- database or the actual DDL statements that create these
- database objects. Some database implementations support what
- would be more accurately called a <em>database namespace</em>
- but is also called a schema. In this sense, a schema is a
- separate namespace in which tables, indexes, sequences, etc.,
- can be created. For example, two tables that have the same
- name can coexist in the same database if they belong to
- different schemas. In this section when we talk about a
- schema, we refer to the <em>database namespace</em> meaning
- of this term. </p>
-
- <p>When schemas are in use, a database object name is qualified
- with a schema. For example:</p>
-
- <pre class="sql">
-CREATE TABLE accounting.employee (...)
-
-SELECT ... FROM accounting.employee WHERE ...
- </pre>
-
- <p>In the above example <code>accounting</code> is the schema
- and the <code>employee</code> table belongs to this
- schema.</p>
-
- <p>Not all database implementations support schemas. Some
- implementation that don't support schemas (for example,
- MySQL, SQLite) allow the use of the above syntax to specify
- the database name. Yet others may support several levels
- of qualification. For example, Microsoft SQL Server has
- three levels starting with the linked database server,
- followed by the database, and then followed by
- the schema:
- <code>server1.company1.accounting.employee</code>.
- While the actual meaning of the qualifier in a qualified name
- vary from one database implementation to another, here we
- refer to all of them collectively as a schema.</p>
-
- <p>In ODB, a schema for a table of a persistent class can be
- specified at the class level, C++ namespace level, or the
- file level. To assign a schema to a specific persistent class
- we can use the <code>schema</code> specifier, for example:</p>
-
- <pre class="cxx">
-#pragma db object schema("accounting")
-class employee
-{
- ...
-};
- </pre>
-
- <p>If we are also assigning a table name, then we can use
- a shorter notation by specifying both the schema and
- the table name in the <code>table</code> specifier:</p>
-
- <pre class="cxx">
-#pragma db object table("accounting.employee")
-class employee
-{
- ...
-};
- </pre>
-
- <p>If we want to assign a schema to all the persistent classes
- in a C++ namespace, then, instead of specifying the schema
- for each class, we can specify it once at the C++ namespace level.
- For example:</p>
-
- <pre class="cxx">
-#pragma db namespace schema("accounting")
-namespace accounting
-{
- #pragma db object
- class employee
- {
- ...
- };
-
- #pragma db object
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>If we want to assign a schema to all the persistent classes in
- a file, then we can use the <code>--schema</code> ODB compiler
- option. For example:</p>
-
- <pre class="terminal">
-odb ... --schema accounting ...
- </pre>
-
- <p>An alternative to this approach with the same effect is to
- assign a schema to the global namespace:</p>
-
- <pre class="cxx">
-#pragma db namespace() schema("accounting")
- </pre>
-
- <p>By default schema qualifications are accumulated starting from
- the persistent class, continuing with the namespace hierarchy
- to which this class belongs, and finishing with the schema
- specified with the <code>--schema</code> option. For
- example:</p>
-
- <pre class="cxx">
-#pragma db namespace schema("audit_db")
-namespace audit
-{
- #pragma db namespace schema("accounting")
- namespace accounting
- {
- #pragma db object
- class employee
- {
- ...
- };
- }
-}
- </pre>
-
- <p>If we compile the above code fragment with the
- <code>--schema&nbsp;server1</code> option, then the
- <code>employee</code> table will have the
- <code>server1.audit_db.accounting.employee</code> qualified
- name.</p>
-
- <p>In some situations we may want to prevent such accumulation
- of the qualifications. To accomplish this we can use the
- so-called fully-qualified names, which have the empty leading
- name component. This is analogous to the C++ fully-qualified
- names in the <code>::accounting::employee</code> form. For
- example:</p>
-
- <pre class="cxx">
-#pragma db namespace schema("accounting")
-namespace accounting
-{
- #pragma db object schema(".hr")
- class employee
- {
- ...
- };
-
- #pragma db object
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>In the above code fragment, the <code>employee</code> table will
- have the <code>hr.employee</code> qualified name while the
- <code>employer</code> &mdash; <code>accounting.employer</code>.
- Note also that the empty leading name component is a special
- ODB syntax and is not propagated to the actual database names
- (using a name like <code>.hr.employee</code> to refer to a table
- will most likely result in an error).</p>
-
- <p>Auxiliary database objects for a persistent class, such as indexes,
- sequences, triggers, etc., are all created in the same schema
- as the class table. By default, this is also true for the
- container tables. However, if you need to store a container
- table in a different schema, then you can provide a qualified
- name using the <code>table</code> specifier, for example:</p>
-
- <pre class="cxx">
-#pragma db object table("accounting.employee")
-class employee
-{
- ...
-
- #pragma db object table("operations.projects")
- std::vector&lt;std::string> projects_;
-};
- </pre>
-
- <p>The standard syntax for qualified names used in the
- <code>schema</code> and <code>table</code> specifiers as well
- as the view <code>column</code> specifier (<a href="#14.4.10">Section
- 14.4.10, "<code>column</code> (view)"</a>) has the
- <code>"</code><i>name</i><code>.</code><i>name</i>...<code>"</code>
- form where, as discussed above, the leading name component
- can be empty to denote a fully qualified name. This form, however,
- doesn't work if one of the name components contains periods. To
- support such cases the alternative form is available:
- <code>"</code><i>name</i><code>"."</code><i>name</i><code>"</code>...
- For example:</p>
-
- <pre class="cxx">
-#pragma db object table("accounting_1.2"."employee")
-class employee
-{
- ...
-};
- </pre>
-
- <p>Finally, to specify an unqualified name that contains periods
- we can use the following special syntax:</p>
-
- <pre class="cxx">
-#pragma db object schema(."accounting_1.2") table("employee")
-class employee
-{
- ...
-};
- </pre>
-
- <p>Table prefixes (<a href="#14.5.2">Section 14.5.2, "<code>table</code>"</a>)
- can be used as an alternative to database schemas if the target
- database system does not support schemas.</p>
-
- <h3><a name="14.1.9">14.1.9 <code>polymorphic</code></a></h3>
-
- <p>The <code>polymorphic</code> specifier specifies that the persistent
- class is polymorphic. For more information on polymorphism support,
- refer to <a href="#8">Chapter 8, "Inheritance"</a>.</p>
-
- <h3><a name="14.1.10">14.1.10 <code>session</code></a></h3>
-
- <p>The <code>session</code> specifier specifies whether to enable
- session support for the persistent class. For example:</p>
-
- <pre class="cxx">
-#pragma db object session // Enable.
-class person
-{
- ...
-};
-
-#pragma db object session(true) // Enable.
-class employee
-{
- ...
-};
-
-#pragma db object session(false) // Disable.
-class employer
-{
- ...
-};
- </pre>
-
- <p>Session support is disabled by default unless the
- <code>--generate-session</code> ODB compiler option is specified
- or session support is enabled at the namespace level
- (<a href="#14.5.4">Section 14.5.4, "<code>session</code>"</a>).
- For more information on sessions, refer to <a href="#11">Chapter
- 11, "Session"</a>.</p>
-
- <h3><a name="14.1.11">14.1.11 <code>definition</code></a></h3>
-
- <p>The <code>definition</code> specifier specifies an alternative
- <em>definition location</em> for the persistent class. By
- default, the ODB compiler generates the database support code for
- a persistent class when we compile the header file that
- defines this class. However, if the <code>definition</code>
- specifier is used, then the ODB compiler will instead generate
- the database support code when we compile the header file
- containing this pragma.</p>
-
- <p>For more information on this functionality, refer to
- <a href="#14.3.7">Section 14.3.7, "<code>definition</code>"</a>.</p>
-
- <h3><a name="14.1.12">14.1.12 <code>transient</code></a></h3>
-
- <p>The <code>transient</code> specifier instructs the ODB compiler to
- treat all non-virtual data members in the persistent class as transient
- (<a href="#14.4.1">Section 14.4.1, "<code>transient</code>"</a>).
- This specifier is primarily useful when declaring virtual data
- members, as discussed in <a href="#14.4.13">Section 14.4.13,
- "<code>virtual</code>"</a>.</p>
-
- <h3><a name="14.1.13">14.1.13 <code>sectionable</code></a></h3>
-
- <p>The <code>sectionable</code> specifier instructs the ODB compiler
- to generate support for the addition of new object sections in
- derived classes in a hierarchy with the optimistic concurrency
- model. For more information on this functionality, refer to
- <a href="#9.2">Section 9.2, "Sections and Optimistic
- Concurrency"</a>.</p>
-
- <h3><a name="14.1.14">14.1.14 <code>deleted</code></a></h3>
-
- <p>The <code>deleted</code> specifier marks the persistent class as
- soft-deleted. The single required argument to this specifier is
- the deletion version. For more information on this functionality,
- refer to <a href="#13.4">Section 13.4, "Soft Object Model
- Changes"</a>.</p>
-
- <h3><a name="14.1.15">14.1.15 <code>bulk</code></a></h3>
-
- <p>The <code>bulk</code> specifier enables bulk operation support for
- the persistent class. The single required argument to this specifier
- is the batch size. For more information on this functionality, refer
- to <a href="#15.3">Section 15.3, "Bulk Database Operations"</a>.</p>
-
- <h2><a name="14.2">14.2 View Type Pragmas</a></h2>
-
- <p>A pragma with the <code>view</code> qualifier declares a C++ class
- as a view type. The qualifier can be optionally followed,
- in any order, by one or more specifiers summarized in the
- table below:</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table class="specifiers" border="1">
- <tr>
- <th>Specifier</th>
- <th>Summary</th>
- <th>Section</th>
- </tr>
-
- <tr>
- <td><code>object</code></td>
- <td>object associated with a view</td>
- <td><a href="#14.2.1">14.2.1</a></td>
- </tr>
-
- <tr>
- <td><code>table</code></td>
- <td>table associated with a view</td>
- <td><a href="#14.2.2">14.2.2</a></td>
- </tr>
-
- <tr>
- <td><code>query</code></td>
- <td>view query condition</td>
- <td><a href="#14.2.3">14.2.3</a></td>
- </tr>
-
- <tr>
- <td><code>pointer</code></td>
- <td>pointer type for a view</td>
- <td><a href="#14.2.4">14.2.4</a></td>
- </tr>
-
- <tr>
- <td><code>callback</code></td>
- <td>database operations callback</td>
- <td><a href="#14.2.5">14.2.5</a></td>
- </tr>
-
- <tr>
- <td><code>definition</code></td>
- <td>definition location for a view</td>
- <td><a href="#14.2.6">14.2.6</a></td>
- </tr>
-
- <tr>
- <td><code>transient</code></td>
- <td>all non-virtual data members in a view are transient</td>
- <td><a href="#14.2.7">14.2.7</a></td>
- </tr>
-
- </table>
-
- <p>For more information on view types refer to <a href="#10"> Chapter 10,
- "Views"</a>.</p>
-
- <h3><a name="14.2.1">14.2.1 <code>object</code></a></h3>
-
- <p>The <code>object</code> specifier specifies a persistent class
- that should be associated with the view. For more information
- on object associations refer to <a href="#10.1">Section 10.1, "Object
- Views"</a>.</p>
-
- <h3><a name="14.2.2">14.2.2 <code>table</code></a></h3>
-
- <p>The <code>table</code> specifier specifies a database table
- that should be associated with the view. For more information
- on table associations refer to <a href="#10.3">Section 10.3, "Table
- Views"</a>.</p>
-
- <h3><a name="14.2.3">14.2.3 <code>query</code></a></h3>
-
- <p>The <code>query</code> specifier specifies a query condition
- and, optionally, result modifiers for an object or table view
- or a native SQL query for a native view. An empty <code>query</code>
- specifier indicates that a native SQL query is provided at runtime.
- For more information on query conditions refer to
- <a href="#10.5">Section 10.5, "View Query Conditions"</a>. For
- more information on native SQL queries, refer to
- <a href="#10.6">Section 10.6, "Native Views"</a>.</p>
-
- <h3><a name="14.2.4">14.2.4 <code>pointer</code></a></h3>
-
- <p>The <code>pointer</code> specifier specifies the view pointer type
- for the view class. Similar to objects, the view pointer type is used
- to return dynamically allocated instances of a view class. The
- semantics of the <code>pointer</code> specifier for a view are the
- same as those of the <code>pointer</code> specifier for an object
- (<a href="#14.1.2">Section 14.1.2, "<code>pointer</code>"</a>).</p>
-
- <h3><a name="14.2.5">14.2.5 <code>callback</code></a></h3>
-
- <p>The <code>callback</code> specifier specifies the view class
- member function that should be called before and after an
- instance of this view class is created as part of the query
- result iteration. The semantics of the <code>callback</code>
- specifier for a view are similar to those of the
- <code>callback</code> specifier for an object
- (<a href="#14.1.7">Section 14.1.7, "<code>callback</code>"</a>)
- except that the only events that can trigger a callback
- call in the case of a view are <code>pre_load</code> and
- <code>post_load</code>.</p>
-
- <h3><a name="14.2.6">14.2.6 <code>definition</code></a></h3>
-
- <p>The <code>definition</code> specifier specifies an alternative
- <em>definition location</em> for the view class. By
- default, the ODB compiler generates the database support code for
- a view class when we compile the header file that
- defines this class. However, if the <code>definition</code>
- specifier is used, then the ODB compiler will instead generate
- the database support code when we compile the header file
- containing this pragma.</p>
-
- <p>For more information on this functionality, refer to
- <a href="#14.3.7">Section 14.3.7, "<code>definition</code>"</a>.</p>
-
- <h3><a name="14.2.7">14.2.7 <code>transient</code></a></h3>
-
- <p>The <code>transient</code> specifier instructs the ODB compiler
- to treat all non-virtual data members in the view class as transient
- (<a href="#14.4.1">Section 14.4.1, "<code>transient</code>"</a>).
- This specifier is primarily useful when declaring virtual data
- members, as discussed in <a href="#14.4.13">Section 14.4.13,
- "<code>virtual</code>"</a>.</p>
-
- <h2><a name="14.3">14.3 Value Type Pragmas</a></h2>
-
- <p>A pragma with the <code>value</code> qualifier describes a value
- type. It can be optionally followed, in any order, by one or more
- specifiers summarized in the table below:</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table class="specifiers" border="1">
- <tr>
- <th>Specifier</th>
- <th>Summary</th>
- <th>Section</th>
- </tr>
-
- <tr>
- <td><code>type</code></td>
- <td>database type for a value type</td>
- <td><a href="#14.3.1">14.3.1</a></td>
- </tr>
-
- <tr>
- <td><code>id_type</code></td>
- <td>database type for a value type when used as an object id</td>
- <td><a href="#14.3.2">14.3.2</a></td>
- </tr>
-
- <tr>
- <td><code>null</code>/<code>not_null</code></td>
- <td>type can/cannot be <code>NULL</code></td>
- <td><a href="#14.3.3">14.3.3</a></td>
- </tr>
-
- <tr>
- <td><code>default</code></td>
- <td>default value for a value type</td>
- <td><a href="#14.3.4">14.3.4</a></td>
- </tr>
-
- <tr>
- <td><code>options</code></td>
- <td>database options for a value type</td>
- <td><a href="#14.3.5">14.3.5</a></td>
- </tr>
-
- <tr>
- <td><code>readonly</code></td>
- <td>composite value type is read-only</td>
- <td><a href="#14.3.6">14.3.6</a></td>
- </tr>
-
- <tr>
- <td><code>definition</code></td>
- <td>definition location for a composite value type</td>
- <td><a href="#14.3.7">14.3.7</a></td>
- </tr>
-
- <tr>
- <td><code>transient</code></td>
- <td>all non-virtual data members in a composite value are transient</td>
- <td><a href="#14.3.8">14.3.8</a></td>
- </tr>
-
- <tr>
- <td><code>unordered</code></td>
- <td>ordered container should be stored unordered</td>
- <td><a href="#14.3.9">14.3.9</a></td>
- </tr>
-
- <tr>
- <td><code>index_type</code></td>
- <td>database type for a container's index type</td>
- <td><a href="#14.3.10">14.3.10</a></td>
- </tr>
-
- <tr>
- <td><code>key_type</code></td>
- <td>database type for a container's key type</td>
- <td><a href="#14.3.11">14.3.11</a></td>
- </tr>
-
- <tr>
- <td><code>value_type</code></td>
- <td>database type for a container's value type</td>
- <td><a href="#14.3.12">14.3.12</a></td>
- </tr>
-
- <tr>
- <td><code>value_null</code>/<code>value_not_null</code></td>
- <td>container's value can/cannot be <code>NULL</code></td>
- <td><a href="#14.3.13">14.3.13</a></td>
- </tr>
-
- <tr>
- <td><code>id_options</code></td>
- <td>database options for a container's id column</td>
- <td><a href="#14.3.14">14.3.14</a></td>
- </tr>
-
- <tr>
- <td><code>index_options</code></td>
- <td>database options for a container's index column</td>
- <td><a href="#14.3.15">14.3.15</a></td>
- </tr>
-
- <tr>
- <td><code>key_options</code></td>
- <td>database options for a container's key column</td>
- <td><a href="#14.3.16">14.3.16</a></td>
- </tr>
-
- <tr>
- <td><code>value_options</code></td>
- <td>database options for a container's value column</td>
- <td><a href="#14.3.17">14.3.17</a></td>
- </tr>
-
- <tr>
- <td><code>id_column</code></td>
- <td>column name for a container's object id</td>
- <td><a href="#14.3.18">14.3.18</a></td>
- </tr>
-
- <tr>
- <td><code>index_column</code></td>
- <td>column name for a container's index</td>
- <td><a href="#14.3.19">14.3.19</a></td>
- </tr>
-
- <tr>
- <td><code>key_column</code></td>
- <td>column name for a container's key</td>
- <td><a href="#14.3.20">14.3.20</a></td>
- </tr>
-
- <tr>
- <td><code>value_column</code></td>
- <td>column name for a container's value</td>
- <td><a href="#14.3.21">14.3.21</a></td>
- </tr>
-
- </table>
-
- <p>Many of the value type specifiers have corresponding member type
- specifiers with the same names (<a href="#14.4">Section 14.4,
- "Data Member Pragmas"</a>). The behavior of such specifiers
- for members is similar to that for value types. The only difference
- is the scope. A particular value type specifier applies to all the
- members of this value type that don't have a pre-member version
- of the specifier, while the member specifier always applies only
- to a single member. Also, with a few exceptions, member specifiers
- take precedence over and override parameters specified with value
- specifiers.</p>
-
- <h3><a name="14.3.1">14.3.1 <code>type</code></a></h3>
-
- <p>The <code>type</code> specifier specifies the native database type
- that should be used for data members of this type. For example:</p>
-
- <pre class="cxx">
-#pragma db value(bool) type("INT")
-
-#pragma db object
-class person
-{
- ...
-
- bool married_; // Mapped to INT NOT NULL database type.
-};
- </pre>
-
- <p>The ODB compiler provides the default mapping between common C++
- types, such as <code>bool</code>, <code>int</code>, and
- <code>std::string</code> and the database types for each supported
- database system. For more information on the default mapping,
- refer to <a href="#II">Part II, "Database Systems"</a>. The
- <code>null</code> and <code>not_null</code> (<a href="#14.3.3">Section
- 14.3.3, "<code>null</code>/<code>not_null</code>"</a>) specifiers
- can be used to control the <code>NULL</code> semantics of a type.</p>
-
- <p>In the above example we changed the mapping for the <code>bool</code>
- type which is now mapped to the <code>INT</code> database type. In
- this case, the <code>value</code> pragma is all that is necessary
- since the ODB compiler will be able to figure out how to store
- a boolean value as an integer in the database. However, there
- could be situations where the ODB compiler will not know how to
- handle the conversion between the C++ and database representations
- of a value. Consider, as an example, a situation where the
- boolean value is stored in the database as a string:</p>
-
- <pre class="cxx">
-#pragma db value(bool) type("VARCHAR(5)")
- </pre>
-
- <p>The possible database values for the C++ <code>true</code> value could
- be <code>"true"</code>, or <code>"TRUE"</code>, or <code>"True"</code>.
- Or, maybe, all of the above could be valid. The ODB compiler has no way
- of knowing how your application wants to convert <code>bool</code>
- to a string and back. To support such custom value type mappings,
- ODB allows you to provide your own database conversion functions
- by specializing the <code>value_traits</code> class template. The
- <code>mapping</code> example in the <code>odb-examples</code>
- package shows how to do this for all the supported database systems.</p>
-
- <h3><a name="14.3.2">14.3.2 <code>id_type</code></a></h3>
-
- <p>The <code>id_type</code> specifier specifies the native database type
- that should be used for data members of this type that are designated as
- object identifiers (<a href="#14.4.1">Section 14.4.1,
- "<code>id</code>"</a>). In combination with the <code>type</code>
- specifier (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>)
- <code>id_type</code> allows you to map a C++ type differently depending
- on whether it is used in an ordinary member or an object id. For
- example:</p>
-
- <pre class="cxx">
-#pragma db value(std::string) type("TEXT") id_type("VARCHAR(64)")
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id
- std::string email_; // Mapped to VARCHAR(64) NOT NULL.
-
- std::string name_; // Mapped to TEXT NOT NULL.
-};
- </pre>
-
- <p>Note that there is no corresponding member type specifier for
- <code>id_type</code> since the desired result can be achieved
- with just the <code>type</code> specifier, for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db id type("VARCHAR(128)")
- std::string email_;
-};
- </pre>
-
- <h3><a name="14.3.3">14.3.3 <code>null</code>/<code>not_null</code></a></h3>
-
- <p>The <code>null</code> and <code>not_null</code> specifiers specify that
- a value type or object pointer can or cannot be <code>NULL</code>,
- respectively. By default, value types are assumed not to allow
- <code>NULL</code> values while object pointers are assumed to
- allow <code>NULL</code> values. Data members of types that allow
- <code>NULL</code> values are mapped in a relational database to
- columns that allow <code>NULL</code> values. For example:</p>
-
- <pre class="cxx">
-using std::tr1::shared_ptr;
-
-typedef shared_ptr&lt;std::string> string_ptr;
-#pragma db value(string_ptr) type("TEXT") null
-
-#pragma db object
-class person
-{
- ...
-
- string_ptr name_; // Mapped to TEXT NULL.
-};
-
-typedef shared_ptr&lt;person> person_ptr;
-#pragma db value(person_ptr) not_null
- </pre>
-
- <p>The <code>NULL</code> semantics can also be specified on the
- per-member basis (<a href="#14.4.6">Section 14.4.6,
- "<code>null</code>/<code>not_null</code>"</a>). If both a type and
- a member have <code>null</code>/<code>not_null</code> specifiers,
- then the member specifier takes precedence. If a member specifier
- relaxes the <code>NULL</code> semantics (that is, if a member has
- the <code>null</code> specifier and the type has the explicit
- <code>not_null</code> specifier), then a warning is issued.</p>
-
- <p>It is also possible to override a previously specified
- <code>null</code>/<code>not_null</code> specifier. This is
- primarily useful if a third-party type, for example,
- one provided by a profile library (<a href="#III">Part III,
- "Profiles"</a>), allows <code>NULL</code> values but in your
- object model data members of this type should never be
- <code>NULL</code>. In this case you can use the <code>not_null</code>
- specifier to disable <code>NULL</code> values for this type for the
- entire translation unit. For example:</p>
-
- <pre class="cxx">
-// By default, null_string allows NULL values.
-//
-#include &lt;null-string.hxx>
-
-// Disable NULL values for all the null_string data members.
-//
-#pragma db value(null_string) not_null
- </pre>
-
- <p>For a more detailed discussion of the <code>NULL</code> semantics
- for values, refer to <a href="#7.3">Section 7.3, "Pointers and
- <code>NULL</code> Value Semantics"</a>. For a more detailed
- discussion of the <code>NULL</code> semantics for object pointers,
- refer to <a href="#6">Chapter 6, "Relationships"</a>.</p>
-
- <h3><a name="14.3.4">14.3.4 <code>default</code></a></h3>
-
- <p>The <code>default</code> specifier specifies the database default value
- that should be used for data members of this type. For example:</p>
-
- <pre class="cxx">
-#pragma db value(std::string) default("")
-
-#pragma db object
-class person
-{
- ...
-
- std::string name_; // Mapped to TEXT NOT NULL DEFAULT ''.
-};
- </pre>
-
- <p>The semantics of the <code>default</code> specifier for a value type
- are similar to those of the <code>default</code> specifier for a
- data member (<a href="#14.4.7">Section 14.4.7,
- "<code>default</code>"</a>).</p>
-
- <h3><a name="14.3.5">14.3.5 <code>options</code></a></h3>
-
- <p>The <code>options</code> specifier specifies additional column
- definition options that should be used for data members of this
- type. For example:</p>
-
- <pre class="cxx">
-#pragma db value(std::string) options("COLLATE binary")
-
-#pragma db object
-class person
-{
- ...
-
- std::string name_; // Mapped to TEXT NOT NULL COLLATE binary.
-};
- </pre>
-
- <p>The semantics of the <code>options</code> specifier for a value type
- are similar to those of the <code>options</code> specifier for a
- data member (<a href="#14.4.8">Section 14.4.8,
- "<code>options</code>"</a>).</p>
-
- <h3><a name="14.3.6">14.3.6 <code>readonly</code></a></h3>
-
- <p>The <code>readonly</code> specifier specifies that the composite
- value type is read-only. Changes to data members of a read-only
- composite value type are ignored when updating the database
- state of an object (<a href="#3.10">Section 3.10, "Updating Persistent
- Objects"</a>) containing such a value type. Note that this specifier
- is only valid for composite value types. For example:</p>
-
- <pre class="cxx">
-#pragma db value readonly
-class person_name
-{
- ...
-};
- </pre>
-
- <p>Read-only and read-write composite values can derive from each other
- without any restrictions. When a read-only value derives from a
- read-write value, the resulting whole value is read-only, including
- the part corresponding to the read-write base. On the other hand, when a
- read-write value derives from a read-only value, all the data
- members that correspond to the read-only base are treated as
- read-only while the rest is treated as read-write.</p>
-
- <p>Note that it is also possible to declare individual data members
- (<a href="#14.4.12">Section 14.4.12, "<code>readonly</code>"</a>)
- as well as whole objects (<a href="#14.1.4">Section 14.1.4,
- "<code>readonly</code>"</a>) as read-only.</p>
-
- <h3><a name="14.3.7">14.3.7 <code>definition</code></a></h3>
-
- <p>The <code>definition</code> specifier specifies an alternative
- <em>definition location</em> for the composite value type. By
- default, the ODB compiler generates the database support code for
- a composite value type when we compile the header file that
- defines this value type. However, if the <code>definition</code>
- specifier is used, then the ODB compiler will instead generate
- the database support code when we compile the header file containing
- this pragma.</p>
-
- <p>This mechanism is primarily useful for converting third-party
- types to ODB composite value types. In such cases we normally
- cannot modify the header files to add the necessary pragmas.
- It is also often inconvenient to compile these header files
- with the ODB compiler. With the <code>definition</code>
- specifier we can create a <em>wrapper header</em> that contains
- the necessary pragmas and instructs the ODB compiler to generate
- the database support code for a third-party type when we compile
- the wrapper header. As an example, consider <code>struct timeval</code>
- that is defined in the <code>&lt;sys/time.h></code> system header.
- This type has the following (or similar) definition:</p>
-
- <pre class="cxx">
-struct timeval
-{
- long tv_sec;
- long tv_usec;
-};
- </pre>
-
- <p>If we would like to make this type an ODB composite value type,
- then we can create a wrapper header, for example
- <code>time-mapping.hxx</code>, with the following content:</p>
-
- <pre class="cxx">
-#ifndef TIME_MAPPING_HXX
-#define TIME_MAPPING_HXX
-
-#include &lt;sys/time.h>
-
-#pragma db value(timeval) definition
-#pragma db member(timeval::tv_sec) column("sec")
-#pragma db member(timeval::tv_usec) column("usec")
-
-#endif // TIME_MAPPING_HXX
- </pre>
-
- <p>If we now compile this header with the ODB compiler, the
- resulting <code>time-mapping-odb.?xx</code> files will
- contain the database support code for <code>struct timeval</code>.
- To use <code>timeval</code> in our persistent classes, we simply
- include the <code>time-mapping.hxx</code> header:</p>
-
- <pre class="cxx">
-#include &lt;sys/time.h>
-#include "time-mapping.hxx"
-
-#pragma db object
-class object
-{
- timeval timestamp;
-};
- </pre>
-
- <h3><a name="14.3.8">14.3.8 <code>transient</code></a></h3>
-
- <p>The <code>transient</code> specifier instructs the ODB compiler
- to treat all non-virtual data members in the composite value type
- as transient (<a href="#14.4.1">Section 14.4.1,
- "<code>transient</code>"</a>). This specifier is primarily useful
- when declaring virtual data members, as discussed in
- <a href="#14.4.13">Section 14.4.13, "<code>virtual</code>"</a>.</p>
-
- <h3><a name="14.3.9">14.3.9 <code>unordered</code></a></h3>
-
- <p>The <code>unordered</code> specifier specifies that the ordered
- container should be stored unordered in the database. The database
- table for such a container will not contain the index column
- and the order in which elements are retrieved from the database may
- not be the same as the order in which they were stored. For example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;std::string> names;
-#pragma db value(names) unordered
- </pre>
-
- <p>For a more detailed discussion of ordered containers and their
- storage in the database, refer to <a href="#5.1">Section 5.1,
- "Ordered Containers"</a>.</p>
-
- <h3><a name="14.3.10">14.3.10 <code>index_type</code></a></h3>
-
- <p>The <code>index_type</code> specifier specifies the native
- database type that should be used for the ordered container's
- index column. The semantics of <code>index_type</code>
- are similar to those of the <code>type</code> specifier
- (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>). The native
- database type is expected to be an integer type. For example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;std::string> names;
-#pragma db value(names) index_type("SMALLINT UNSIGNED")
- </pre>
-
- <h3><a name="14.3.11">14.3.11 <code>key_type</code></a></h3>
-
- <p>The <code>key_type</code> specifier specifies the native
- database type that should be used for the map container's
- key column. The semantics of <code>key_type</code>
- are similar to those of the <code>type</code> specifier
- (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>). For
- example:</p>
-
- <pre class="cxx">
-typedef std::map&lt;unsigned short, float> age_weight_map;
-#pragma db value(age_weight_map) key_type("INT UNSIGNED")
- </pre>
-
- <h3><a name="14.3.12">14.3.12 <code>value_type</code></a></h3>
-
- <p>The <code>value_type</code> specifier specifies the native
- database type that should be used for the container's
- value column. The semantics of <code>value_type</code>
- are similar to those of the <code>type</code> specifier
- (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>). For
- example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;std::string> names;
-#pragma db value(names) value_type("VARCHAR(255)")
- </pre>
-
- <p>The <code>value_null</code> and <code>value_not_null</code>
- (<a href="#14.3.13">Section 14.3.13,
- "<code>value_null</code>/<code>value_not_null</code>"</a>) specifiers
- can be used to control the <code>NULL</code> semantics of a value
- column.</p>
-
- <h3><a name="14.3.13">14.3.13 <code>value_null</code>/<code>value_not_null</code></a></h3>
-
- <p>The <code>value_null</code> and <code>value_not_null</code> specifiers
- specify that the container type's element value can or cannot be
- <code>NULL</code>, respectively. The semantics of <code>value_null</code>
- and <code>value_not_null</code> are similar to those of the
- <code>null</code> and <code>not_null</code> specifiers
- (<a href="#14.3.3">Section 14.3.3, "<code>null</code>/<code>not_null</code>"</a>).
- For example:</p>
-
- <pre class="cxx">
-using std::tr1::shared_ptr;
-
-#pragma db object
-class account
-{
- ...
-};
-
-typedef std::vector&lt;shared_ptr&lt;account> > accounts;
-#pragma db value(accounts) value_not_null
- </pre>
-
- <p>For set and multiset containers (<a href="#5.2">Section 5.2, "Set and
- Multiset Containers"</a>) the element value is automatically treated
- as not allowing a <code>NULL</code> value.</p>
-
-
- <h3><a name="14.3.14">14.3.14 <code>id_options</code></a></h3>
-
- <p>The <code>id_options</code> specifier specifies additional
- column definition options that should be used for the container's
- id column. For example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;std::string> nicknames;
-#pragma db value(nicknames) id_options("COLLATE binary")
- </pre>
-
- <p>The semantics of the <code>id_options</code> specifier for a container
- type are similar to those of the <code>id_options</code> specifier for
- a container data member (<a href="#14.4.29">Section 14.4.29,
- "<code>id_options</code>"</a>).</p>
-
-
- <h3><a name="14.3.15">14.3.15 <code>index_options</code></a></h3>
-
- <p>The <code>index_options</code> specifier specifies additional
- column definition options that should be used for the container's
- index column. For example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;std::string> nicknames;
-#pragma db value(nicknames) index_options("ZEROFILL")
- </pre>
-
- <p>The semantics of the <code>index_options</code> specifier for a container
- type are similar to those of the <code>index_options</code> specifier for
- a container data member (<a href="#14.4.30">Section 14.4.30,
- "<code>index_options</code>"</a>).</p>
-
-
- <h3><a name="14.3.16">14.3.16 <code>key_options</code></a></h3>
-
- <p>The <code>key_options</code> specifier specifies additional
- column definition options that should be used for the container's
- key column. For example:</p>
-
- <pre class="cxx">
-typedef std::map&lt;std::string, std::string> properties;
-#pragma db value(properties) key_options("COLLATE binary")
- </pre>
-
- <p>The semantics of the <code>key_options</code> specifier for a container
- type are similar to those of the <code>key_options</code> specifier for
- a container data member (<a href="#14.4.31">Section 14.4.31,
- "<code>key_options</code>"</a>).</p>
-
-
- <h3><a name="14.3.17">14.3.17 <code>value_options</code></a></h3>
-
- <p>The <code>value_options</code> specifier specifies additional
- column definition options that should be used for the container's
- value column. For example:</p>
-
- <pre class="cxx">
-typedef std::set&lt;std::string> nicknames;
-#pragma db value(nicknames) value_options("COLLATE binary")
- </pre>
-
- <p>The semantics of the <code>value_options</code> specifier for a container
- type are similar to those of the <code>value_options</code> specifier for
- a container data member (<a href="#14.4.32">Section 14.4.32,
- "<code>value_options</code>"</a>).</p>
-
-
- <h3><a name="14.3.18">14.3.18 <code>id_column</code></a></h3>
-
- <p>The <code>id_column</code> specifier specifies the column
- name that should be used to store the object id in the
- container's table. For example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;std::string> names;
-#pragma db value(names) id_column("id")
- </pre>
-
- <p>If the column name is not specified, then <code>object_id</code>
- is used by default.</p>
-
- <h3><a name="14.3.19">14.3.19 <code>index_column</code></a></h3>
-
- <p>The <code>index_column</code> specifier specifies the column
- name that should be used to store the element index in the
- ordered container's table. For example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;std::string> names;
-#pragma db value(names) index_column("name_number")
- </pre>
-
- <p>If the column name is not specified, then <code>index</code>
- is used by default.</p>
-
- <h3><a name="14.3.20">14.3.20 <code>key_column</code></a></h3>
-
- <p>The <code>key_column</code> specifier specifies the column
- name that should be used to store the key in the map
- container's table. For example:</p>
-
- <pre class="cxx">
-typedef std::map&lt;unsigned short, float> age_weight_map;
-#pragma db value(age_weight_map) key_column("age")
- </pre>
-
- <p>If the column name is not specified, then <code>key</code>
- is used by default.</p>
-
- <h3><a name="14.3.21">14.3.21 <code>value_column</code></a></h3>
-
- <p>The <code>value_column</code> specifier specifies the column
- name that should be used to store the element value in the
- container's table. For example:</p>
-
- <pre class="cxx">
-typedef std::map&lt;unsigned short, float> age_weight_map;
-#pragma db value(age_weight_map) value_column("weight")
- </pre>
-
- <p>If the column name is not specified, then <code>value</code>
- is used by default.</p>
-
- <!-- Data Member Pragmas -->
-
-
- <h2><a name="14.4">14.4 Data Member Pragmas</a></h2>
-
- <p>A pragma with the <code>member</code> qualifier or a positioned
- pragma without a qualifier describes a data member. It can
- be optionally followed, in any order, by one or more specifiers
- summarized in the table below:</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table class="specifiers" border="1">
- <tr>
- <th>Specifier</th>
- <th>Summary</th>
- <th>Section</th>
- </tr>
-
- <tr>
- <td><code>id</code></td>
- <td>member is an object id</td>
- <td><a href="#14.4.1">14.4.1</a></td>
- </tr>
-
- <tr>
- <td><code>auto</code></td>
- <td>id is assigned by the database</td>
- <td><a href="#14.4.2">14.4.2</a></td>
- </tr>
-
- <tr>
- <td><code>type</code></td>
- <td>database type for a member</td>
- <td><a href="#14.4.3">14.4.3</a></td>
- </tr>
-
- <tr>
- <td><code>id_type</code></td>
- <td>database type for a member when used as an object id</td>
- <td><a href="#14.4.4">14.4.4</a></td>
- </tr>
-
- <tr>
- <td><code>get</code>/<code>set</code>/<code>access</code></td>
- <td>member accessor/modifier expressions</td>
- <td><a href="#14.4.5">14.4.5</a></td>
- </tr>
-
- <tr>
- <td><code>null</code>/<code>not_null</code></td>
- <td>member can/cannot be <code>NULL</code></td>
- <td><a href="#14.4.6">14.4.6</a></td>
- </tr>
-
- <tr>
- <td><code>default</code></td>
- <td>default value for a member</td>
- <td><a href="#14.4.7">14.4.7</a></td>
- </tr>
-
- <tr>
- <td><code>options</code></td>
- <td>database options for a member</td>
- <td><a href="#14.4.8">14.4.8</a></td>
- </tr>
-
- <tr>
- <td><code>column</code></td>
- <td>column name for a member of an object or composite value</td>
- <td><a href="#14.4.9">14.4.9</a></td>
- </tr>
-
- <tr>
- <td><code>column</code></td>
- <td>column name for a member of a view</td>
- <td><a href="#14.4.10">14.4.10</a></td>
- </tr>
-
- <tr>
- <td><code>transient</code></td>
- <td>member is not stored in the database</td>
- <td><a href="#14.4.11">14.4.11</a></td>
- </tr>
-
- <tr>
- <td><code>readonly</code></td>
- <td>member is read-only</td>
- <td><a href="#14.4.12">14.4.12</a></td>
- </tr>
-
- <tr>
- <td><code>virtual</code></td>
- <td>declare a virtual data member</td>
- <td><a href="#14.4.13">14.4.13</a></td>
- </tr>
-
- <tr>
- <td><code>inverse</code></td>
- <td>member is an inverse side of a bidirectional relationship</td>
- <td><a href="#14.4.14">14.4.14</a></td>
- </tr>
-
- <tr>
- <td><code>on_delete</code></td>
- <td><code>ON DELETE</code> clause for object pointer member</td>
- <td><a href="#14.4.15">14.4.15</a></td>
- </tr>
-
- <tr>
- <td><code>version</code></td>
- <td>member stores object version</td>
- <td><a href="#14.4.16">14.4.16</a></td>
- </tr>
-
- <tr>
- <td><code>index</code></td>
- <td>define database index for a member</td>
- <td><a href="#14.4.17">14.4.17</a></td>
- </tr>
-
- <tr>
- <td><code>unique</code></td>
- <td>define unique database index for a member</td>
- <td><a href="#14.4.18">14.4.18</a></td>
- </tr>
-
- <tr>
- <td><code>unordered</code></td>
- <td>ordered container should be stored unordered</td>
- <td><a href="#14.4.19">14.4.19</a></td>
- </tr>
-
- <tr>
- <td><code>table</code></td>
- <td>table name for a container</td>
- <td><a href="#14.4.20">14.4.20</a></td>
- </tr>
-
- <tr>
- <td><code>load</code>/<code>update</code></td>
- <td>loading/updating behavior for a section</td>
- <td><a href="#14.4.21">14.4.21</a></td>
- </tr>
-
- <tr>
- <td><code>section</code></td>
- <td>member belongs to a section</td>
- <td><a href="#14.4.22">14.4.22</a></td>
- </tr>
-
- <tr>
- <td><code>added</code></td>
- <td>member is soft-added</td>
- <td><a href="#14.4.23">14.4.23</a></td>
- </tr>
-
- <tr>
- <td><code>deleted</code></td>
- <td>member is soft-deleted</td>
- <td><a href="#14.4.24">14.4.24</a></td>
- </tr>
-
- <tr>
- <td><code>index_type</code></td>
- <td>database type for a container's index type</td>
- <td><a href="#14.4.25">14.4.25</a></td>
- </tr>
-
- <tr>
- <td><code>key_type</code></td>
- <td>database type for a container's key type</td>
- <td><a href="#14.4.26">14.4.26</a></td>
- </tr>
-
- <tr>
- <td><code>value_type</code></td>
- <td>database type for a container's value type</td>
- <td><a href="#14.4.27">14.4.27</a></td>
- </tr>
-
- <tr>
- <td><code>value_null</code>/<code>value_not_null</code></td>
- <td>container's value can/cannot be <code>NULL</code></td>
- <td><a href="#14.4.28">14.4.28</a></td>
- </tr>
-
- <tr>
- <td><code>id_options</code></td>
- <td>database options for a container's id column</td>
- <td><a href="#14.4.29">14.4.29</a></td>
- </tr>
-
- <tr>
- <td><code>index_options</code></td>
- <td>database options for a container's index column</td>
- <td><a href="#14.4.30">14.4.30</a></td>
- </tr>
-
- <tr>
- <td><code>key_options</code></td>
- <td>database options for a container's key column</td>
- <td><a href="#14.4.31">14.4.31</a></td>
- </tr>
-
- <tr>
- <td><code>value_options</code></td>
- <td>database options for a container's value column</td>
- <td><a href="#14.4.32">14.4.32</a></td>
- </tr>
-
- <tr>
- <td><code>id_column</code></td>
- <td>column name for a container's object id</td>
- <td><a href="#14.4.33">14.4.33</a></td>
- </tr>
-
- <tr>
- <td><code>index_column</code></td>
- <td>column name for a container's index</td>
- <td><a href="#14.4.34">14.4.34</a></td>
- </tr>
-
- <tr>
- <td><code>key_column</code></td>
- <td>column name for a container's key</td>
- <td><a href="#14.4.35">14.4.35</a></td>
- </tr>
-
- <tr>
- <td><code>value_column</code></td>
- <td>column name for a container's value</td>
- <td><a href="#14.4.36">14.4.36</a></td>
- </tr>
-
- </table>
-
- <p>Many of the member specifiers have corresponding value type
- specifiers with the same names (<a href="#14.3">Section 14.3,
- "Value Type Pragmas"</a>). The behavior of such specifiers
- for members is similar to that for value types. The only difference
- is the scope. A particular value type specifier applies to all the
- members of this value type that don't have a pre-member version
- of the specifier, while the member specifier always applies only
- to a single member. Also, with a few exceptions, member specifiers
- take precedence over and override parameters specified with value
- specifiers.</p>
-
- <h3><a name="14.4.1">14.4.1 <code>id</code></a></h3>
-
- <p>The <code>id</code> specifier specifies that the data member contains
- the object id. In a relational database, an identifier member is
- mapped to a primary key. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db id
- std::string email_;
-};
- </pre>
-
- <p>Normally, every persistent class has a data member designated as an
- object's identifier. However, it is possible to declare a
- persistent class without an id using the object <code>no_id</code>
- specifier (<a href="#14.1.6">Section 14.1.6, "<code>no_id</code>"</a>).</p>
-
- <p>Note also that the <code>id</code> specifier cannot be used for data
- members of composite value types or views.</p>
-
- <h3><a name="14.4.2">14.4.2 <code>auto</code></a></h3>
-
- <p>The <code>auto</code> specifier specifies that the object's identifier
- is automatically assigned by the database. Only a member that was
- designated as an object id can have this specifier. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-};
- </pre>
-
- <p>Note that automatically-assigned object ids are not reused.
- If you have a high object turnover (that is, objects are routinely
- made persistent and then erased), then care must be taken not to
- run out of object ids. In such situations, using
- <code>unsigned&nbsp;long&nbsp;long</code> as the identifier type
- is a safe choice.</p>
-
- <p>For additional information on the automatic identifier assignment,
- refer to <a href="#3.8">Section 3.8, "Making Objects Persistent"</a>.</p>
-
- <p>Note also that the <code>auto</code> specifier cannot be specified
- for data members of composite value types or views.</p>
-
- <h3><a name="14.4.3">14.4.3 <code>type</code></a></h3>
-
- <p>The <code>type</code> specifier specifies the native database type
- that should be used for the data member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db type("INT")
- bool married_;
-};
- </pre>
-
- <p>The <code>null</code> and <code>not_null</code> (<a href="#14.4.6">Section
- 14.4.6, "<code>null</code>/<code>not_null</code>"</a>) specifiers
- can be used to control the <code>NULL</code> semantics of a data member.
- It is also possible to specify the database type on the per-type instead
- of the per-member basis using the value <code>type</code>
- specifier (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>).</p>
-
- <h3><a name="14.4.4">14.4.4 <code>id_type</code></a></h3>
-
- <p>The <code>id_type</code> specifier specifies the native database type
- that should be used for the data member when it is part of an
- object identifier. This specifier only makes sense when applied to
- a member of a composite value type that is used for both id and
- non-id members. For example:</p>
-
- <pre class="cxx">
-#pragma db value
-class name
-{
- ...
-
- #pragma db type("VARCHAR(256)") id_type("VARCHAR(64)")
- std::string first_;
-
- #pragma db type("VARCHAR(256)") id_type("VARCHAR(64)")
- std::string last_;
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id
- name name_; // name_.first_, name_.last_ mapped to VARCHAR(64)
-
- name alias_; // alias_.first_, alias_.last_ mapped to VARCHAR(256)
-};
- </pre>
-
- <h3><a name="14.4.5">14.4.5 <code>get</code>/<code>set</code>/<code>access</code></a></h3>
-
- <p>The <code>get</code> and <code>set</code> specifiers specify the
- data member accessor and modifier expressions, respectively. If
- provided, the generated database support code will use these
- expressions to access and modify the data member when performing
- database operations. The <code>access</code> specifier can be used
- as a shortcut to specify both the accessor and modifier if they
- happen to be the same.</p>
-
- <p>In its simplest form the accessor or modifier expression can be
- just a name. Such a name should resolve either to another data
- member of the same type or to a suitable accessor or modifier
- member function. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
-public:
- const std::string&amp; name () const;
- void name (const std::string&amp;);
-private:
- #pragma db access(name)
- std::string name_;
-};
- </pre>
-
- <p>A suitable accessor function is a <code>const</code> member function
- that takes no arguments and whose return value can be implicitly
- converted to the <code>const</code> reference to the member type
- (<code>const&nbsp;std::string&amp;</code> in the example above).
- An accessor function that returns a <code>const</code> reference
- to the data member is called <em>by-reference accessor</em>.
- Otherwise, it is called <em>by-value accessor</em>.</p>
-
- <p>A suitable modifier function can be of two forms. It can be the
- so called <em>by-reference modifier</em> which is a member function
- that takes no arguments and returns a non-<code>const</code> reference
- to the data member (<code>std::string&amp;</code> in the example above).
- Alternatively, it can be the so called <em>by-value modifier</em> which
- is a member function taking a single argument &mdash; the new value
- &mdash; that can be implicitly initialized from a variable of the member
- type (<code>std::string</code> in the example above). The return value
- of a by-value modifier, if any, is ignored. If both by-reference and
- by-value modifiers are available, then ODB prefers the by-reference
- version since it is more efficient. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
-public:
- std::string get_name () const; // By-value accessor.
- std::string&amp; set_name (); // By-reference modifier.
- void set_name (std::string const&amp;); // By-value modifier.
-private:
- #pragma db get(get_name) \ // Uses by-value accessor.
- set(set_name) // Uses by-reference modifier.
- std::string name_;
-};
- </pre>
-
- <p>Note that in many cases it is not necessary to specify accessor and
- modifier functions explicitly since the ODB compiler will try to
- discover them automatically in case the data member will be inaccessible
- to the generated code. In particular, in both of the above examples
- the ODB compiler would have successfully discovered the necessary
- functions. For more information on this functionality, refer to
- <a href="#3.2">Section 3.2, "Declaring Persistent Objects and
- Values"</a>.</p>
-
- <p>Note also that by-value accessors and by-value modifiers cannot be
- used for certain data members in certain situations. These limitations
- are discussed in more detail later in this section.</p>
-
- <p>Accessor and modifier expressions can be more elaborate than simple
- names. An accessor expression is any C++ expression that can be
- used to initialize a <code>const</code> reference to the member
- type. Similar to accessor functions, which are just a special case
- of accessor expressions, an accessor expression that evaluates to a
- <code>const</code> reference to the data member is called
- <em>by-reference accessor expression</em>. Otherwise, it is
- called <em>by-value accessor expression</em>.</p>
-
- <p>Modifier expressions can also be of two forms: <em>by-reference
- modifier expression</em> and <em>by-value modifier expression</em>
- (again, modifier functions are just a special case of modifier
- expressions). A by-reference modifier expression is any C++
- expression that evaluates to the non-<code>const</code> reference
- to the member type. A by-value modifier expression can be a
- single or multiple (separated by semicolon) C++ statements
- with the effect of setting the new member value.</p>
-
- <p>There are two special placeholders that are recognized by the
- ODB compiler in accessor and modifier expressions. The first
- is the <code>this</code> keyword which denotes a reference
- (note: not a pointer) to the persistent object. In accessor
- expressions this reference is <code>const</code> while in
- modifier expressions it is non-<code>const</code>. If an
- expression does not contain the <code>this</code> placeholder,
- then the ODB compiler automatically prefixes it with <code>this.</code>
- sequence.</p>
-
- <p>The second placeholder, the <code>(?)</code> sequence, is used
- to denote the new value in by-value modifier expressions. The
- ODB compiler replaces the question mark with the variable name,
- keeping the surrounding parenthesis. The following example shows
- a few more interesting accessor and modifier expressions:</p>
-
- <pre class="cxx">
-#pragma db value
-struct point
-{
- point (int, int);
-
- int x;
- int y;
-};
-
-#pragma db object
-class person
-{
- ...
-
- public:
- const char* name () const;
- void name (const char*);
- private:
- #pragma db get(std::string (this.name ())) \
- set(name ((?).c_str ())) // The same as this.name (...).
- std::string name_;
-
- public:
- const std::unique_ptr&lt;account>&amp; acc () const;
- void acc (std::unique_ptr&lt;account>);
- private:
- #pragma db set(acc (std::move (?)))
- std::unique_ptr&lt;account> acc_;
-
- public:
- int loc_x () const
- int loc_y () const
- void loc_x (int);
- void loc_y (int);
- private:
- #pragma db get(point (this.loc_x (), this.loc_y ())) \
- set(this.loc_x ((?).x); this.loc_y ((?).y))
- point loc_;
-};
- </pre>
-
- <p>When the data member is of an array type, then the terms "reference"
- and "member type" in the above discussion should be replaced with
- "pointer" and "array element type", respectively. That is, the accessor
- expression for an array member is any C++ expression that can be
- used to initialize a <code>const</code> pointer to the array
- element type, and so on. The following example shows common
- accessor and modifier signatures for array members:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- public:
- const char* id () const; // By-reference accessor.
- void id (const char*); // By-value modifier.
- private:
- char id_[16];
-
- public:
- const char* pub_key () const; // By-reference accessor.
- char* pub_key (); // By-reference modifier.
- private:
- char pub_key_[2048];
-};
- </pre>
-
- <p>Accessor and modifier expressions can be used with data members
- of simple value, composite value, container, and object pointer
- types. They can be used for data members in persistent classes,
- composite value types, and views. There is also a mechanism
- related to accessors and modifiers called virtual data members
- and which is discussed in <a href="#14.4.13">Section 14.4.13,
- "<code>virtual</code>"</a>.</p>
-
- <p>There are, however, certain limitations when it comes to using
- by-value accessor and modifier expressions. First of all, if a
- by-value modifier is used, then the data member type should be
- default-constructible. Furthermore, a composite value type that
- has a container member cannot be modified with a by-value modifier.
- Only a by-reference modifier expression can be used. The ODB
- compiler will detect such cases and issue diagnostics. For
- example:</p>
-
- <pre class="cxx">
-#pragma db value
-struct name
-{
- std::string first_;
- std::string last_;
- std::vector&lt;std::string> aliases_;
-};
-
-#pragma db object
-class person
-{
- ...
-
-public:
- const name&amp; name () const;
- void name (const name&amp;);
-private:
- #pragma db access(name) // Error: by-value modifier.
- name name_;
-};
- </pre>
-
- <p>In certain database systems it is also not possible to use by-value
- accessor and modifier expression with certain database types.
- The ODB compiler is only able to detect such cases and issue diagnostics
- if you specified accessor/modifier function names as opposed to custom
- expressions. For more information on these database and type-specific
- limitations, refer to the "Limitations" sections in <a href="#II">Part
- II, "Database Systems"</a>.</p>
-
- <h3><a name="14.4.6">14.4.6 <code>null</code>/<code>not_null</code></a></h3>
-
- <p>The <code>null</code> and <code>not_null</code> specifiers specify that
- the data member can or cannot be <code>NULL</code>, respectively.
- By default, data members of basic value types for which database
- mapping is provided by the ODB compiler do not allow <code>NULL</code>
- values while data members of object pointers allow <code>NULL</code>
- values. Other value types, such as those provided by the profile
- libraries (<a href="#III">Part III, "Profiles"</a>), may or may
- not allow <code>NULL</code> values, depending on the semantics
- of each value type. Consult the relevant documentation to find
- out more about the <code>NULL</code> semantics for such value
- types. A data member containing the object id (<a href="#14.4.1">Section
- 14.4.1, "<code>id</code>"</a>) is automatically treated as not
- allowing a <code>NULL</code> value. Data members that
- allow <code>NULL</code> values are mapped in a relational database
- to columns that allow <code>NULL</code> values. For example:</p>
-
- <pre class="cxx">
-using std::tr1::shared_ptr;
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db null
- std::string name_;
-};
-
-#pragma db object
-class account
-{
- ...
-
- #pragma db not_null
- shared_ptr&lt;person> holder_;
-};
- </pre>
-
- <p>The <code>NULL</code> semantics can also be specified on the
- per-type basis (<a href="#14.3.3">Section 14.3.3,
- "<code>null</code>/<code>not_null</code>"</a>). If both a type and
- a member have <code>null</code>/<code>not_null</code> specifiers,
- then the member specifier takes precedence. If a member specifier
- relaxes the <code>NULL</code> semantics (that is, if a member has
- the <code>null</code> specifier and the type has the explicit
- <code>not_null</code> specifier), then a warning is issued.</p>
-
- <p>For a more detailed discussion of the <code>NULL</code> semantics
- for values, refer to <a href="#7.3">Section 7.3, "Pointers and
- <code>NULL</code> Value Semantics"</a>. For a more detailed
- discussion of the <code>NULL</code> semantics for object pointers,
- refer to <a href="#6">Chapter 6, "Relationships"</a>.</p>
-
- <h3><a name="14.4.7">14.4.7 <code>default</code></a></h3>
-
- <p>The <code>default</code> specifier specifies the database default value
- that should be used for the data member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db default(-1)
- int age_; // Mapped to INT NOT NULL DEFAULT -1.
-};
- </pre>
-
- <p>A default value can be the special <code>null</code> keyword,
- a <code>bool</code> literal (<code>true</code> or <code>false</code>),
- an integer literal, a floating point literal, a string literal, or
- an enumerator name. If you need to specify a default value that is
- an expression, for example an SQL function call, then you can use
- the <code>options</code> specifier (<a href="#14.4.8">Section
- 14.4.8, "<code>options</code>"</a>) instead. For example:</p>
-
- <pre class="cxx">
-enum gender {male, female, undisclosed};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db default(null)
- odb::nullable&lt;std::string> middle_; // DEFAULT NULL
-
- #pragma db default(false)
- bool married_; // DEFAULT 0/FALSE
-
- #pragma db default(0.0)
- float weight_; // DEFAULT 0.0
-
- #pragma db default("Mr")
- string title_; // DEFAULT 'Mr'
-
- #pragma db default(undisclosed)
- gender gender_; // DEFAULT 2/'undisclosed'
-
- #pragma db options("DEFAULT CURRENT_TIMESTAMP()")
- date timestamp_; // DEFAULT CURRENT_TIMESTAMP()
-};
- </pre>
-
- <p>Default values specified as enumerators are only supported for
- members that are mapped to an <code>ENUM</code> or an integer
- type in the database, which is the case for the automatic
- mapping of C++ enums and enum classes to suitable database
- types as performed by the ODB compiler. If you have mapped
- a C++ enum or enum class to another database type, then you
- should use a literal corresponding to that type to specify
- the default value. For example:</p>
-
- <pre class="cxx">
-enum gender {male, female, undisclosed};
-#pragma db value(gender) type("VARCHAR(11)")
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db default("undisclosed")
- gender gender_; // DEFAULT 'undisclosed'
-};
- </pre>
-
- <p>A default value can also be specified on the per-type basis
- (<a href="#14.3.4">Section 14.3.4, "<code>default</code>"</a>).
- An empty <code>default</code> specifier can be used to reset
- a default value that was previously specified on the per-type
- basis. For example:</p>
-
- <pre class="cxx">
-#pragma db value(std::string) default("")
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db default()
- std::string name_; // No default value.
-};
- </pre>
-
- <p>A data member containing the object id (<a href="#14.4.1">Section
- 14.4.1, "<code>id</code>"</a> ) is automatically treated as not
- having a default value even if its type specifies a default value.</p>
-
- <p>Note also that default values do not affect the generated C++ code
- in any way. In particular, no automatic initialization of data members
- with their default values is performed at any point. If you need such
- an initialization, you will need to implement it yourself, for example,
- in your persistent class constructors. The default values only
- affect the generated database schemas and, in the context of ODB,
- are primarily useful for schema evolution.</p>
-
- <p>Additionally, the <code>default</code> specifier cannot be specified
- for view data members.</p>
-
- <h3><a name="14.4.8">14.4.8 <code>options</code></a></h3>
-
- <p>The <code>options</code> specifier specifies additional column
- definition options that should be used for the data member. For
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db options("CHECK(email != '')")
- std::string email_; // Mapped to TEXT NOT NULL CHECK(email != '').
-};
- </pre>
-
- <p>Options can also be specified on the per-type basis
- (<a href="#14.3.5">Section 14.3.5, "<code>options</code>"</a>).
- By default, options are accumulating. That is, the ODB compiler
- first adds all the options specified for a value type followed
- by all the options specified for a data member. To clear the
- accumulated options at any point in this sequence you can use
- an empty <code>options</code> specifier. For example:</p>
-
- <pre class="cxx">
-#pragma db value(std::string) options("COLLATE binary")
-
-#pragma db object
-class person
-{
- ...
-
- std::string first_; // TEXT NOT NULL COLLATE binary
-
- #pragma db options("CHECK(last != '')")
- std::string last_; // TEXT NOT NULL COLLATE binary CHECK(last != '')
-
- #pragma db options()
- std::string title_; // TEXT NOT NULL
-
- #pragma db options() options("CHECK(email != '')")
- std::string email_; // TEXT NOT NULL CHECK(email != '')
-};
- </pre>
-
- <p>ODB provides dedicated specifiers for specifying column types
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>),
- <code>NULL</code> constraints (<a href="#14.4.6">Section 14.4.6,
- "<code>null</code>/<code>not_null</code>"</a>), and default
- values (<a href="#14.4.7">Section 14.4.7, "<code>default</code>"</a>).
- For ODB to function correctly these specifiers should always be
- used instead of the opaque <code>options</code> specifier for
- these components of a column definition.</p>
-
- <p>Note also that the <code>options</code> specifier cannot be specified
- for view data members.</p>
-
- <h3><a name="14.4.9">14.4.9 <code>column</code> (object, composite value)</a></h3>
-
- <p>The <code>column</code> specifier specifies the column name
- that should be used to store the data member of a persistent class
- or composite value type in a relational database. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db id column("person_id")
- unsigned long id_;
-};
- </pre>
-
- <p>For a member of a composite value type, the <code>column</code> specifier
- specifies the column name prefix. Refer to <a href="#7.2.2">Section 7.2.2,
- "Composite Value Column and Table Names"</a> for details.</p>
-
- <p>If the column name is not specified, it is derived from the member's
- so-called public name. A public member name is obtained by removing
- the common data member name decorations, such as leading and trailing
- underscores, the <code>m_</code> prefix, etc.</p>
-
- <h3><a name="14.4.10">14.4.10 <code>column</code> (view)</a></h3>
-
- <p>The <code>column</code> specifier can be used to specify the associated
- object data member, the potentially qualified column name, or the column
- expression for the data member of a view class. For more information,
- refer to <a href="#10.1">Section 10.1, "Object Views"</a> and
- <a href="#10.3">Section 10.3, "Table Views"</a>.</p>
-
- <h3><a name="14.4.11">14.4.11 <code>transient</code></a></h3>
-
- <p>The <code>transient</code> specifier instructs the ODB compiler
- not to store the data member in the database. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- date born_;
-
- #pragma db transient
- unsigned short age_; // Computed from born_.
-};
- </pre>
-
- <p>This pragma is usually used on computed members, pointers and
- references that are only meaningful in the application's
- memory, as well as utility members such as mutexes, etc.</p>
-
- <h3><a name="14.4.12">14.4.12 <code>readonly</code></a></h3>
-
- <p>The <code>readonly</code> specifier specifies that the data member of
- an object or composite value type is read-only. Changes to a read-only
- data member are ignored when updating the database state of an object
- (<a href="#3.10">Section 3.10, "Updating Persistent Objects"</a>)
- containing such a member. Since views are read-only, it is not
- necessary to use this specifier for view data members. Object id
- (<a href="#14.4.1">Section 14.4.1, "<code>id</code>"</a>)
- and inverse (<a href="#14.4.14">Section 14.4.14,
- "<code>inverse</code>"</a>) data members are automatically treated
- as read-only and must not be explicitly declared as such. For
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db readonly
- date born_;
-};
- </pre>
-
- <p>Besides simple value members, object pointer, container, and composite
- value members can also be declared read-only. A change of a pointed-to
- object is ignored when updating the state of a read-only object
- pointer. Similarly, any changes to the number or order of
- elements or to the element values themselves are ignored when
- updating the state of a read-only container. Finally, any changes
- to the members of a read-only composite value type are also ignored
- when updating the state of such a composite value.</p>
-
- <p>ODB automatically treats <code>const</code> data members as read-only.
- For example, the following <code>person</code> object is equivalent
- to the above declaration for the database persistence purposes:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- const date born_; // Automatically read-only.
-};
- </pre>
-
- <p>When declaring an object pointer <code>const</code>, make sure to
- declare the pointer as <code>const</code> rather than (or in addition
- to) the object itself. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- const person* father_; // Read-write pointer to a read-only object.
- person* const mother_; // Read-only pointer to a read-write object.
-};
- </pre>
-
- <p>Note that in case of a wrapper type (<a href="#7.3">Section 7.3,
- "Pointers and <code>NULL</code> Value Semantics"</a>), both the
- wrapper and the wrapped type must be <code>const</code> in
- order for the ODB compiler to automatically treat the data
- member as read-only. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- const std::auto_ptr&lt;const date> born_;
-};
- </pre>
-
- <p>Read-only members are useful when dealing with
- asynchronous changes to the state of a data member in the
- database which should not be overwritten. In other cases,
- where the state of a data member never changes, declaring such a member
- read-only allows ODB to perform more efficient object updates.
- In such cases, however, it is conceptually more correct to
- declare such a data member as <code>const</code> rather than
- as read-only.</p>
-
- <p>Note that it is also possible to declare composite value types
- (<a href="#14.3.6">Section 14.3.6, "<code>readonly</code>"</a>)
- as well as whole objects (<a href="#14.1.4">Section 14.1.4,
- "<code>readonly</code>"</a>) as read-only.</p>
-
- <h3><a name="14.4.13">14.4.13 <code>virtual</code></a></h3>
-
- <p>The <code>virtual</code> specifier is used to declare a virtual
- data member in an object, view, or composite value type. A virtual
- data member is an <em>imaginary</em> data member that is only
- used for the purpose of database persistence. A virtual data
- member does not actually exist (that is, occupy space) in the
- C++ class. Note also that virtual data members have nothing to
- do with C++ virtual functions or virtual inheritance. Specifically,
- no virtual function call overhead is incurred when using virtual
- data members.</p>
-
- <p>To declare a virtual data member we must specify the data
- member name using the <code>member</code> specifier. We must
- also specify the data member type with the <code>virtual</code>
- specifier. Finally, the virtual data member declaration must
- also specify the accessor and modifier expressions, unless
- suitable accessor and modifier functions can automatically be
- found by the ODB compiler (<a href="#14.4.5">Section 14.4.5,
- "<code>get</code>/<code>set</code>/<code>access</code>"</a>).
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- // Transient real data member that actually stores the data.
- //
- #pragma db transient
- std::string name_;
-
- // Virtual data member.
- //
- #pragma db member(name) virtual(std::string) access(name_)
-};
- </pre>
-
- <p>From the pragma language point of view, a virtual data member
- behaves exactly like a normal data member. Specifically, we
- can reference the virtual data member after it has been
- declared and use positioned pragmas before its declaration.
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db transient
- std::string name_;
-
- #pragma db access(name_)
- #pragma db member(name) virtual(std::string)
-};
-
-#pragma db member(person::name) column("person_name")
-#pragma db index member(person::name)
- </pre>
-
- <p>We can also declare a virtual data member outside the class
- scope:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- std::string name_;
-};
-
-#pragma db member(person::name_) transient
-#pragma db member(person::name) virtual(std::string) access(name_)
- </pre>
-
- <p>While in the above examples using virtual data members doesn't
- seem to yield any benefits, this mechanism can be useful in a
- number of situations. As one example, consider the need to
- aggregate or dis-aggregate a data member:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db transient
- std::pair&lt;std::string, std::string> name_;
-
- #pragma db member(first) virtual(std::string) access(name_.first)
- #pragma db member(last) virtual(std::string) access(name_.second)
-};
- </pre>
-
- <p>We can also use virtual data members to implement composite
- object ids that are spread over multiple data members:</p>
-
- <pre class="cxx">
-#pragma db value
-struct name
-{
- name () {}
- name (std::string const&amp; f, std::string const&amp; l)
- : first (f), last(l) {}
-
- std::string first;
- std::string last;
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db transient
- std::string first_;
-
- #pragma db transient
- std::string last_;
-
- #pragma db member(name) virtual(name) id \
- get(::name (this.first_, this.last_)) \
- set(this.first_ = (?).first; this.last_ = (?).last)
-};
- </pre>
-
- <p>Another common situation that calls for virtual data members is
- a class that uses the pimpl idiom. While the following code
- fragment outlines the idea, for details refer to the
- <code>pimpl</code> example in the <code>odb-examples</code>
- package.</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
-public:
- std::string const&amp; name () const;
- void name (std::string const&amp;);
-
- unsigned short age () const;
- void age (unsigned short);
-
- ...
-
-private:
- class impl;
-
- #pragma db transient
- impl* pimpl_;
-
- #pragma db member(name) virtual(std::string) // Uses name().
- #pragma db member(age) virtual(unsigned short) // Uses age().
-};
- </pre>
-
- <p>The above example also shows that names used for virtual data
- members (<code>name</code> and <code>age</code> in our case) can
- be the same as the names of accessor/modifier functions. The only
- names that virtual data members cannot clash with are those of
- other data members, virtual or real.</p>
-
- <p>A common pattern in the above examples is the need to
- declare the real data member that actually stores the
- data as transient. If all the real data members in a
- class are treated as transient, then we can use the
- class-level <code>transient</code> specifier
- (<a href="#14.1.12">Section 14.1.12, "<code>transient</code>
- (object)"</a>,
- <a href="#14.3.8">Section 14.3.8, "<code>transient</code>
- (composite value)"</a>,
- <a href="#14.2.7">Section 14.2.7, "<code>transient</code>
- (view)"</a>)
- instead of doing it for each individual member. For example: </p>
-
- <pre class="cxx">
-#pragma db object transient
-class person
-{
- ...
-
- std::string first_; // Transient.
- std::string last_; // Transient.
-
- #pragma db member(name) virtual(name) ...
-};
- </pre>
-
- <p>The ability to treat all the real data members as transient
- becomes more important if we don't know the names of these
- data members. This is often the case when we are working
- with third-party types that document the accessor and
- modifier functions but not the names of their private data
- members. As an example, consider the <code>point</code> class
- defined in a third-party <code>&lt;point></code> header file:</p>
-
- <pre class="cxx">
-class point
-{
-public:
- point ();
- point (int x, int y);
-
- int x () const;
- int y () const;
-
- void x (int);
- void y (int);
-
-private:
- ...
-};
- </pre>
-
- <p>To convert this class to an ODB composite value type we could
- create the <code>point-mapping.hxx</code> file with the following
- content:</p>
-
- <pre class="cxx">
-#include &lt;point>
-
-#pragma db value(point) transient definition
-#pragma db member(point::x) virtual(int)
-#pragma db member(point::y) virtual(int)
- </pre>
-
- <p>Virtual data members can be of simple value, composite value,
- container, or object pointer types. They can be used in persistent
- classes, composite value types, and views.</p>
-
- <h3><a name="14.4.14">14.4.14 <code>inverse</code></a></h3>
-
- <p>The <code>inverse</code> specifier specifies that the data member of
- an object pointer or a container of object pointers type is an
- inverse side of a bidirectional object relationship. The single
- required argument to this specifier is the corresponding data
- member name in the referenced object. For example:</p>
-
- <pre class="cxx">
-using std::tr1::shared_ptr;
-using std::tr1::weak_ptr;
-
-class person;
-
-#pragma db object pointer(shared_ptr)
-class employer
-{
- ...
-
- std::vector&lt;shared_ptr&lt;person> > employees_;
-};
-
-#pragma db object pointer(shared_ptr)
-class person
-{
- ...
-
- #pragma db inverse(employee_)
- weak_ptr&lt;employer> employer_;
-};
- </pre>
-
- <p>An inverse member does not have a corresponding column or, in case
- of a container, table in the resulting database schema. Instead, the
- column or table from the referenced object is used to retrieve the
- relationship information. Only ordered and set containers can be used
- for inverse members. If an inverse member is of an ordered container
- type, it is automatically marked as unordered
- (<a href="#14.4.19">Section 14.4.19, "<code>unordered</code>"</a>).</p>
-
- <p>For a more detailed discussion of inverse members, refer to
- <a href="#6.2">Section 6.2, "Bidirectional Relationships"</a>.</p>
-
- <h3><a name="14.4.15">14.4.15 <code>on_delete</code></a></h3>
-
- <p>The <code>on_delete</code> specifier specifies the on-delete semantics
- for a data member of an object pointer or a container of object
- pointers type. The single required argument to this specifier must
- be either <code>cascade</code> or <code>set_null</code>.</p>
-
- <p>The <code>on_delete</code> specifier is translated directly to the
- corresponding <code>ON DELETE</code> SQL clause. That is, if
- <code>cascade</code> is specified, then when a pointed-to object
- is erased from the database, the database state of the pointing
- object is automatically erased as well. If <code>set_null</code> is
- specified, then when a pointed-to object is erased from the database,
- the database state of the pointing object is automatically updated
- to set the pointer column to <code>NULL</code>. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class employer
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db on_delete(cascade)
- employer* employer_;
-};
-
-unsigned long id;
-
-{
- employer e;
- person p;
- p.employer_ = &amp;e;
-
- transaction t (db.begin ());
-
- id = db.persist (e);
- db.persist (p);
-
- t.commit ();
-}
-
-{
- transaction t (db.begin ());
-
- // Database state of the person object is erased as well.
- //
- db.erase&lt;employer> (id);
-
- t.commit ();
-}
- </pre>
-
-
- <p>Note that this is a database-level functionality and care must be
- taken in order not to end up with inconsistent object states in the
- application's memory and database. The following example illustrates
- the kind of problems one may encounter:</p>
-
- <pre class="cxx">
-#pragma db object
-class employer
-{
- ...
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db on_delete(set_null)
- employer* employer_;
-};
-
-employer e;
-person p;
-p.employer_ = &amp;e;
-
-{
- transaction t (db.begin ());
- db.persist (e);
- db.persist (p);
- t.commit ();
-}
-
-{
- transaction t (db.begin ());
-
- // The employer column is set to NULL in the database but
- // not the p.employer_ data member in the application.
- //
- db.erase (e);
- t.commit ();
-}
-
-{
- transaction t (db.begin ());
-
- // Override the employer column with an invalid pointer.
- //
- db.update (p);
-
- t.commit ();
-}
- </pre>
-
- <p>Note that even optimistic concurrency will not resolve such
- issues unless you are using database-level support for optimistic
- concurrency as well (for example, <code>ROWVERSION</code> in SQL
- Server).</p>
-
- <p>The <code>on_delete</code> specifier is only valid for non-inverse
- object pointer data members. If the <code>set_null</code> semantics
- is used, then the pointer must allow the <code>NULL</code> value.</p>
-
- <h3><a name="14.4.16">14.4.16 <code>version</code></a></h3>
-
- <p>The <code>version</code> specifier specifies that the data member stores
- the object version used to support optimistic concurrency. If a class
- has a version data member, then it must also be declared as having the
- optimistic concurrency model using the <code>optimistic</code> pragma
- (<a href="#14.1.5">Section 14.1.5, "<code>optimistic</code>"</a>). For
- example:</p>
-
- <pre class="cxx">
-#pragma db object optimistic
-class person
-{
- ...
-
- #pragma db version
- unsigned long version_;
-};
- </pre>
-
- <p>A version member must be of an integral C++ type and must map to
- an integer or similar database type. Note also that object versions
- are not reused. If you have a high update frequency, then care must
- be taken not to run out of versions. In such situations, using
- <code>unsigned&nbsp;long&nbsp;long</code> as the version type is a safe
- choice.</p>
-
- <p>For a more detailed discussion of optimistic concurrency, refer to
- <a href="#12">Chapter 12, "Optimistic Concurrency"</a>.</p>
-
- <h3><a name="14.4.17">14.4.17 <code>index</code></a></h3>
-
- <p>The <code>index</code> specifier instructs the ODB compiler to define
- a database index for the data member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db index
- std::string name_;
-};
- </pre>
-
- <p>For more information on defining database indexes, refer to
- <a href="#14.7">Section 14.7, "Index Definition Pragmas"</a>.</p>
-
- <h3><a name="14.4.18">14.4.18 <code>unique</code></a></h3>
-
- <p>The <code>index</code> specifier instructs the ODB compiler to define
- a unique database index for the data member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db unique
- std::string name_;
-};
- </pre>
-
- <p>For more information on defining database indexes, refer to
- <a href="#14.7">Section 14.7, "Index Definition Pragmas"</a>.</p>
-
- <h3><a name="14.4.19">14.4.19 <code>unordered</code></a></h3>
-
- <p>The <code>unordered</code> specifier specifies that the member of
- an ordered container type should be stored unordered in the database.
- The database table for such a member will not contain the index column
- and the order in which elements are retrieved from the database may
- not be the same as the order in which they were stored. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db unordered
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>For a more detailed discussion of ordered containers and their
- storage in the database, refer to <a href="#5.1">Section 5.1,
- "Ordered Containers"</a>.</p>
-
- <h3><a name="14.4.20">14.4.20 <code>table</code></a></h3>
-
- <p>The <code>table</code> specifier specifies the table name that should
- be used to store the contents of the container member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db table("nicknames")
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>If the table name is not specified, then the container table name
- is constructed by concatenating the object's table name, underscore,
- and the public member name. The public member name is obtained
- by removing the common member name decorations, such as leading and
- trailing underscores, the <code>m_</code> prefix, etc. In the example
- above, without the <code>table</code> specifier, the container's
- table name would have been <code>person_nicknames</code>.</p>
-
- <p>The <code>table</code> specifier can also be used for members of
- composite value types. In this case it specifies the table name
- prefix for container members inside the composite value type. Refer
- to <a href="#7.2.2">Section 7.2.2, "Composite Value Column and Table
- Names"</a> for details.</p>
-
- <p>The container table name can be qualified with a database
- schema, for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db table("extras.nicknames")
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>For more information on database schemas and the format of the
- qualified names, refer to <a href="#14.1.8">Section 14.1.8,
- "<code>schema</code>"</a>.</p>
-
- <h3><a name="14.4.21">14.4.21 <code>load</code>/<code>update</code></a></h3>
-
- <p>The <code>load</code> and <code>update</code> specifiers specify the
- loading and updating behavior for an object section, respectively.
- Valid values for the <code>load</code> specifier are
- <code>eager</code> (default) and <code>lazy</code>. Valid values for
- the <code>update</code> specifier are <code>always</code> (default),
- <code>change</code>, and <code>manual</code>. For more information
- on object sections, refer to <a href="#9">Chapter 9, "Sections"</a>.</p>
-
- <h3><a name="14.4.22">14.4.22 <code>section</code></a></h3>
-
- <p>The <code>section</code> specifier indicates that a data member
- of a persistent class belongs to an object section. The single
- required argument to this specifier is the name of the section
- data member. This specifier can only be used on direct data
- members of a persistent class. For more information on object
- sections, refer to <a href="#9">Chapter 9, "Sections"</a>.</p>
-
- <h3><a name="14.4.23">14.4.23 <code>added</code></a></h3>
-
- <p>The <code>added</code> specifier marks the data member as
- soft-added. The single required argument to this specifier is
- the addition version. For more information on this functionality,
- refer to <a href="#13.4">Section 13.4, "Soft Object Model
- Changes"</a>.</p>
-
- <h3><a name="14.4.24">14.4.24 <code>deleted</code></a></h3>
-
- <p>The <code>deleted</code> specifier marks the data member as
- soft-deleted. The single required argument to this specifier is
- the deletion version. For more information on this functionality,
- refer to <a href="#13.4">Section 13.4, "Soft Object Model
- Changes"</a>.</p>
-
- <h3><a name="14.4.25">14.4.25 <code>index_type</code></a></h3>
-
- <p>The <code>index_type</code> specifier specifies the native
- database type that should be used for an ordered container's
- index column of the data member. The semantics of <code>index_type</code>
- are similar to those of the <code>type</code> specifier
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>). The native
- database type is expected to be an integer type. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db index_type("SMALLINT UNSIGNED")
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <h3><a name="14.4.26">14.4.26 <code>key_type</code></a></h3>
-
- <p>The <code>key_type</code> specifier specifies the native
- database type that should be used for a map container's
- key column of the data member. The semantics of <code>key_type</code>
- are similar to those of the <code>type</code> specifier
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>). For
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db key_type("INT UNSIGNED")
- std::map&lt;unsigned short, float> age_weight_map_;
-};
- </pre>
-
- <h3><a name="14.4.27">14.4.27 <code>value_type</code></a></h3>
-
- <p>The <code>value_type</code> specifier specifies the native
- database type that should be used for a container's
- value column of the data member. The semantics of <code>value_type</code>
- are similar to those of the <code>type</code> specifier
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>). For
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db value_type("VARCHAR(255)")
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>The <code>value_null</code> and <code>value_not_null</code>
- (<a href="#14.4.28">Section 14.4.28,
- "<code>value_null</code>/<code>value_not_null</code>"</a>) specifiers
- can be used to control the <code>NULL</code> semantics of a value
- column.</p>
-
- <h3><a name="14.4.28">14.4.28 <code>value_null</code>/<code>value_not_null</code></a></h3>
-
- <p>The <code>value_null</code> and <code>value_not_null</code> specifiers
- specify that a container's element value for the data member can or
- cannot be <code>NULL</code>, respectively. The semantics of
- <code>value_null</code> and <code>value_not_null</code> are similar
- to those of the <code>null</code> and <code>not_null</code> specifiers
- (<a href="#14.4.6">Section 14.4.6, "<code>null</code>/<code>not_null</code>"</a>).
- For example:</p>
-
- <pre class="cxx">
-using std::tr1::shared_ptr;
-
-#pragma db object
-class person
-{
- ...
-};
-
-#pragma db object
-class account
-{
- ...
-
- #pragma db value_not_null
- std::vector&lt;shared_ptr&lt;person> > holders_;
-};
- </pre>
-
- <p>For set and multiset containers (<a href="#5.2">Section 5.2, "Set and
- Multiset Containers"</a>) the element value is automatically treated
- as not allowing a <code>NULL</code> value.</p>
-
- <h3><a name="14.4.29">14.4.29 <code>id_options</code></a></h3>
-
- <p>The <code>id_options</code> specifier specifies additional
- column definition options that should be used for a container's
- id column of the data member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db id options("COLLATE binary")
- std::string name_;
-
- #pragma db id_options("COLLATE binary")
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>The semantics of <code>id_options</code> are similar to those
- of the <code>options</code> specifier (<a href="#14.4.8">Section
- 14.4.8, "<code>options</code>"</a>).</p>
-
- <h3><a name="14.4.30">14.4.30 <code>index_options</code></a></h3>
-
- <p>The <code>index_options</code> specifier specifies additional
- column definition options that should be used for a container's
- index column of the data member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db index_options("ZEROFILL")
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>The semantics of <code>index_options</code> are similar to those
- of the <code>options</code> specifier (<a href="#14.4.8">Section
- 14.4.8, "<code>options</code>"</a>).</p>
-
- <h3><a name="14.4.31">14.4.31 <code>key_options</code></a></h3>
-
- <p>The <code>key_options</code> specifier specifies additional
- column definition options that should be used for a container's
- key column of the data member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db key_options("COLLATE binary")
- std::map&lt;std::string, std::string> properties_;
-};
- </pre>
-
- <p>The semantics of <code>key_options</code> are similar to those
- of the <code>options</code> specifier (<a href="#14.4.8">Section
- 14.4.8, "<code>options</code>"</a>).</p>
-
- <h3><a name="14.4.32">14.4.32 <code>value_options</code></a></h3>
-
- <p>The <code>value_options</code> specifier specifies additional
- column definition options that should be used for a container's
- value column of the data member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db value_options("COLLATE binary")
- std::set&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>The semantics of <code>value_options</code> are similar to those
- of the <code>options</code> specifier (<a href="#14.4.8">Section
- 14.4.8, "<code>options</code>"</a>).</p>
-
- <h3><a name="14.4.33">14.4.33 <code>id_column</code></a></h3>
-
- <p>The <code>id_column</code> specifier specifies the column
- name that should be used to store the object id in a
- container's table for the data member. The semantics of
- <code>id_column</code> are similar to those of the
- <code>column</code> specifier
- (<a href="#14.4.9">Section 14.4.9, "<code>column</code>"</a>).
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db id_column("person_id")
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>If the column name is not specified, then <code>object_id</code>
- is used by default.</p>
-
- <h3><a name="14.4.34">14.4.34 <code>index_column</code></a></h3>
-
- <p>The <code>index_column</code> specifier specifies the column
- name that should be used to store the element index in an
- ordered container's table for the data member. The semantics of
- <code>index_column</code> are similar to those of the
- <code>column</code> specifier
- (<a href="#14.4.9">Section 14.4.9, "<code>column</code>"</a>).
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db index_column("nickname_number")
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>If the column name is not specified, then <code>index</code>
- is used by default.</p>
-
- <h3><a name="14.4.35">14.4.35 <code>key_column</code></a></h3>
-
- <p>The <code>key_column</code> specifier specifies the column
- name that should be used to store the key in a map
- container's table for the data member. The semantics of
- <code>key_column</code> are similar to those of the
- <code>column</code> specifier
- (<a href="#14.4.9">Section 14.4.9, "<code>column</code>"</a>).
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db key_column("age")
- std::map&lt;unsigned short, float> age_weight_map_;
-};
- </pre>
-
- <p>If the column name is not specified, then <code>key</code>
- is used by default.</p>
-
- <h3><a name="14.4.36">14.4.36 <code>value_column</code></a></h3>
-
- <p>The <code>value_column</code> specifier specifies the column
- name that should be used to store the element value in a
- container's table for the data member. The semantics of
- <code>value_column</code> are similar to those of the
- <code>column</code> specifier
- (<a href="#14.4.9">Section 14.4.9, "<code>column</code>"</a>).
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db value_column("weight")
- std::map&lt;unsigned short, float> age_weight_map_;
-};
- </pre>
-
- <p>If the column name is not specified, then <code>value</code>
- is used by default.</p>
-
- <h2><a name="14.5">14.5 Namespace Pragmas</a></h2>
-
- <p>A pragma with the <code>namespace</code> qualifier describes a
- C++ namespace. Similar to other qualifiers, <code>namespace</code>
- can also refer to a named C++ namespace, for example:</p>
-
- <pre class="cxx">
-namespace test
-{
- ...
-}
-
-#pragma db namespace(test) ...
- </pre>
-
- <p>To refer to the global namespace in the <code>namespace</code>
- qualifier the following special syntax is used:</p>
-
- <pre class="cxx">
-#pragma db namespace() ....
- </pre>
-
- <p>The <code>namespace</code> qualifier can be optionally followed,
- in any order, by one or more specifiers summarized in the
- table below:</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table class="specifiers" border="1">
- <tr>
- <th>Specifier</th>
- <th>Summary</th>
- <th>Section</th>
- </tr>
-
- <tr>
- <td><code>pointer</code></td>
- <td>pointer type for persistent classes and views inside a namespace</td>
- <td><a href="#14.5.1">14.5.1</a></td>
- </tr>
-
- <tr>
- <td><code>table</code></td>
- <td>table name prefix for persistent classes inside a namespace</td>
- <td><a href="#14.5.2">14.5.2</a></td>
- </tr>
-
- <tr>
- <td><code>schema</code></td>
- <td>database schema for persistent classes inside a namespace</td>
- <td><a href="#14.5.3">14.5.3</a></td>
- </tr>
-
- <tr>
- <td><code>session</code></td>
- <td>enable/disable session support for persistent classes inside a namespace</td>
- <td><a href="#14.5.4">14.5.4</a></td>
- </tr>
-
- </table>
-
- <h3><a name="14.5.1">14.5.1 <code>pointer</code></a></h3>
-
- <p>The <code>pointer</code> specifier specifies the default pointer
- type for persistent classes and views inside the namespace. For
- example:</p>
-
- <pre class="cxx">
-#pragma db namespace pointer(std::tr1::shared_ptr)
-namespace accounting
-{
- #pragma db object
- class employee
- {
- ...
- };
-
- #pragma db object
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>There are only two valid ways to specify a pointer with the
- <code>pointer</code> specifier at the namespace level. We can
- specify the template name of a smart pointer in which
- case the ODB compiler will automatically append the class
- name as a template argument. Or we can use <code>*</code>
- to denote a raw pointer.</p>
-
- <p>Note also that we can always override the default pointer
- specified at the namespace level for any persistent class
- or view inside this namespace. For example:</p>
-
- <pre class="cxx">
-#pragma db namespace pointer(std::unique_ptr)
-namespace accounting
-{
- #pragma db object pointer(std::shared_ptr)
- class employee
- {
- ...
- };
-
- #pragma db object
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>For a more detailed discussion of object and view pointers, refer
- to <a href="#3.3">Section 3.3, "Object and View Pointers"</a>.</p>
-
- <h3><a name="14.5.2">14.5.2 <code>table</code></a></h3>
-
- <p>The <code>table</code> specifier specifies a table prefix
- that should be added to table names of persistent classes inside
- the namespace. For example:</p>
-
- <pre class="cxx">
-#pragma db namespace table("acc_")
-namespace accounting
-{
- #pragma db object table("employees")
- class employee
- {
- ...
- };
-
- #pragma db object table("employers")
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>In the above example the resulting table names will be
- <code>acc_employees</code> and <code>acc_employers</code>.</p>
-
- <p>The table name prefix can also be specified with the
- <code>--table-prefix</code> ODB compiler option. Note
- that table prefixes specified at the namespace level as well
- as with the command line option are accumulated. For example:</p>
-
- <pre class="cxx">
-#pragma db namespace() table("audit_")
-
-#pragma db namespace table("hr_")
-namespace hr
-{
- #pragma db object table("employees")
- class employee
- {
- ...
- };
-}
-
-#pragma db object table("employers")
-class employer
-{
- ...
-};
- </pre>
-
- <p>If we compile the above example with the
- <code>--table-prefix&nbsp;test_</code> option, then the
- <code>employee</code> class table will be called
- <code>test_audit_hr_employees</code> and <code>employer</code> &mdash;
- <code>test_audit_employers</code>.</p>
-
- <p>Table prefixes can be used as an alternative to database schemas
- (<a href="#14.1.8">Section 14.1.8, "<code>schema</code>"</a>) if
- the target database system does not support schemas.</p>
-
- <h3><a name="14.5.3">14.5.3 <code>schema</code></a></h3>
-
- <p>The <code>schema</code> specifier specifies a database schema
- that should be used for persistent classes inside the namespace.
- For more information on specifying a database schema refer to
- <a href="#14.1.8">Section 14.1.8, "<code>schema</code>"</a>.</p>
-
- <h3><a name="14.5.4">14.5.4 <code>session</code></a></h3>
-
- <p>The <code>session</code> specifier specifies whether to enable
- session support for persistent classes inside the namespace. For
- example:</p>
-
- <pre class="cxx">
-#pragma db namespace session
-namespace hr
-{
- #pragma db object // Enabled.
- class employee
- {
- ...
- };
-
- #pragma db object session(false) // Disabled.
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>Session support is disabled by default unless the
- <code>--generate-session</code> ODB compiler option is specified.
- Session support specified at the namespace level can be overridden
- on the per object basis (<a href="#14.1.10">Section 14.1.10,
- "<code>session</code>"</a>). For more information on sessions,
- refer to <a href="#11">Chapter 11, "Session"</a>.</p>
-
-<h2><a name="14.6">14.6 Object Model Pragmas</a></h2>
-
- <p>A pragma with the <code>model</code> qualifier describes the
- whole C++ object model. For example:</p>
-
- <pre class="cxx">
-#pragma db model ...
- </pre>
-
- <p>The <code>model</code> qualifier can be followed, in any order,
- by one or more specifiers summarized in the table below:</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table class="specifiers" border="1">
- <tr>
- <th>Specifier</th>
- <th>Summary</th>
- <th>Section</th>
- </tr>
-
- <tr>
- <td><code>version</code></td>
- <td>object model version</td>
- <td><a href="#14.6.1">14.6.1</a></td>
- </tr>
-
- </table>
-
- <h3><a name="14.6.1">14.6.1 <code>version</code></a></h3>
-
- <p>The <code>version</code> specifier specifies the object model
- version when schema evolution support is used. The first two
- required arguments to this specifier are the base and current
- model versions, respectively. The third optional argument
- specifies whether the current version is open for changes.
- Valid values for this argument are <code>open</code> (the
- default) and <code>closed</code>. For more information on
- this functionality, refer to <a href="#13">Chapter 13,
- "Database Schema Evolution"</a>.</p>
-
-
- <h2><a name="14.7">14.7 Index Definition Pragmas</a></h2>
-
- <p>While it is possible to manually add indexes to the generated
- database schema, it is more convenient to do this as part of
- the persistent class definitions. A pragma with the <code>index</code>
- qualifier describes a database index. It has the following
- general format:</p>
-
-<pre class="cxx">
-#pragma db index[("&lt;name>")] \
- [unique|type("&lt;type>")] \
- [method("&lt;method>")] \
- [options("&lt;index-options>")] \
- member(&lt;name>[, "&lt;column-options>"])... \
- members(&lt;name>[,&lt;name>...])...
-</pre>
-
- <p>The <code>index</code> qualifier can optionally specify the
- index name. If the index name is not specified, then one is
- automatically derived by appending the <code>_i</code> suffix
- to the column name of the index member. If the name is not
- specified and the index contains multiple members, then the
- index definition is invalid.</p>
-
- <p>The optional <code>type</code>, <code>method</code>, and
- <code>options</code> clauses specify the index type, for
- example <code>UNIQUE</code>, index method, for example
- <code>BTREE</code>, and index options, respectively. The
- <code>unique</code> clause is a shortcut for
- <code>type("UNIQUE")</code>. Note that not all database
- systems support specifying an index method or options.
- For more information on the database system-specific index
- types, methods, and options, refer to <a href="#II">Part II,
- "Database Systems"</a>.</p>
-
- <p>To specify index members we can use the <code>member</code>
- or <code>members</code> clauses, or a mix of the two. The
- <code>member</code> clause allows us to specify a single
- index member with optional column options, for example,
- <code>"ASC"</code>. If we need to create a composite
- index that contains multiple members, then we can repeat
- the <code>member</code> clause several times or, if the
- members don't have any column options, we can use a single
- <code>members</code> clause instead. Similar to the index
- type, method, and options, the format of column options is
- database system-specific. For more details, refer to
- <a href="#II">Part II, "Database Systems"</a>.</p>
-
- <p>The following code fragment shows some typical examples
- of index definitions:</p>
-
-<pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- int x;
- int y;
- int z1;
- int z2;
-
- // An index for member x with automatically-assigned name x_i.
- //
- #pragma db index member(x)
-
- // A unique index named y_index for member y which is sorted in
- // the descending order. The index is using the BTREE method.
- //
- #pragma db index("y_index") unique method("BTREE") member(y, "DESC")
-
- // A composite BITMAP index named z_i for members z1 and z2.
- //
- #pragma db index("z_i") type("BITMAP") members(z1, z2)
-};
-</pre>
-
- <p>ODB also offers a shortcut for defining an index with the default
- method and options for a single data member. Such an index can
- be defined using the <code>index</code> (<a href="#14.4.17">Section
- 14.4.17, "<code>index</code>"</a>) or <code>unique</code>
- (<a href="#14.4.18">Section 14.4.18, "<code>unique</code>"</a>)
- member specifier. For example:</p>
-
-<pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db index
- int x;
-
- #pragma db type("INT") unique
- int y;
-};
-</pre>
-
- <p>The above example is semantically equivalent to the following
- more verbose version:</p>
-
-<pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- int x;
-
- #pragma db type("INT")
- int y;
-
- #pragma db index member(x)
- #pragma db index unique member(y)
-};
-</pre>
-
- <p>While it is convenient to define an index inside a persistent
- class, it is also possible to do that out of the class body. In this
- case, the index name, if specified, must be prefixed with the
- potentially-qualified class name. For example:</p>
-
-<pre class="cxx">
-namespace n
-{
- #pragma db object
- class object
- {
- ...
-
- int x;
- int y;
- };
-
- // An index for member x in persistent class object with automatically-
- // assigned name x_i.
- //
- #pragma db index(object) member(x)
-}
-
-// An index named y_index for member y in persistent class n::object.
-//
-#pragma db index(n::object::"y_index") member(y)
-</pre>
-
- <p>It is possible to define an index on a member that is of a
- composite value type or on some of its nested members. For
- example:</p>
-
-<pre class="cxx">
-#pragma db value
-struct point
-{
- int x;
- int y;
- int z;
-};
-
-#pragma db object
-class object
-{
- // An index that includes all of the p1's nested members.
- //
- #pragma db index
- point p1;
-
- point p2;
-
- // An index that includes only p2.x and p2.y.
- //
- #pragma db index("p2_xy_i") members(p2.x, p2.y)
-};
-</pre>
-
- <p>When generating a schema for a container member (<a href="#5">Chapter 5,
- "Containers"</a>), ODB automatically defines two indexes on the container
- table. One is for the object id that references the object table and the
- other is for the index column in case the container is ordered
- (<a href="#5.1">Section 5.1, "Ordered Containers"</a>). By default these
- indexes use the default index name, type, method, and options. The
- <code>index</code> pragma allows us to customize these two indexes by
- recognizing the special <code>id</code> and <code>index</code> nested
- member names when specified after a container member. For example:</p>
-
-<pre class="cxx">
-#pragma db object
-class object
-{
- std::vector&lt;int> v;
-
- // Change the container id index name.
- //
- #pragma db index("id_index") member(v.id)
-
- // Change the container index index method.
- //
- #pragma db index method("BTREE") member(v.index)
-};
-</pre>
-
- <h2><a name="14.8">14.8 Database Type Mapping Pragmas</a></h2>
-
- <p>A pragma with the <code>map</code> qualifier describes a
- mapping between two database types. For each database system
- ODB provides built-in support for a core set of database types,
- such as integers, strings, binary, etc. However, many database
- systems provide extended types such as geospatial types,
- user-defined types, and collections (arrays, table types,
- key-value stores, etc). In order to support such extended types,
- ODB allows us to map them to one of the built-in types, normally
- a string or a binary. Given the text or binary representation
- of the data we can then extract it into our chosen C++ data type
- and thus establish a mapping between an extended database type and
- its C++ equivalent.</p>
-
- <p>The <code>map</code> pragma has the following format:</p>
-
-<pre class="cxx">
-#pragma db map type("regex") as("subst") [to("subst")] [from("subst")]
-</pre>
-
- <p>The <code>type</code> clause specifies the name of the database type
- that we are mapping. We will refer to it as the <em>mapped type</em>
- from now on. The name of the mapped type is a Perl-like regular
- expression pattern that is matched in the case-insensitive mode.</p>
-
- <p>The <code>as</code> clause specifies the name of the database type
- that we are mapping the mapped type to. We will refer to it as
- the <em>interface type</em> from now on. The name of the interface
- type is a regular expression substitution and should expand to a
- name of a database type for which ODB provides built-in support.</p>
-
- <p>The optional <code>to</code> and <code>from</code> clauses specify the
- database conversion expressions between the mapped type and the
- interface type. The <code>to</code> expression converts from the
- interface type to the mapped type and <code>from</code> converts
- in the other direction. If no explicit conversion is required for
- either direction, then the corresponding clause can be omitted.
- The conversion expressions are regular expression substitutions.
- They must contain the special <code>(?)</code> placeholder which will
- be replaced with the actual value to be converted. Turning on SQL
- statement tracing (<a href="#3.13">Section 3.13, "Tracing SQL
- Statement Execution"</a>) can be useful for debugging conversion
- expressions. This allows you to see the substituted expressions
- as used in the actual statements.</p>
-
- <p>As an example, the following <code>map</code> pragma maps the
- PostgreSQL array of <code>INTEGER</code>'s to <code>TEXT</code>:</p>
-
-<pre class="cxx">
-#pragma db map type("INTEGER *\\[(\\d*)\\]") \
- as("TEXT") \
- to("(?)::INTEGER[$1]") \
- from("(?)::TEXT")
-</pre>
-
- <p>With the above mapping we can now have a persistent class that
- has a member of the PostgreSQL array type:</p>
-
-<pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("INTEGER[]")
- std::string array_;
-};
-</pre>
-
- <p>In PostgreSQL the array literal has the <code>{n1,n2,...}</code> form.
- As a result, we need to make sure that we pass the correct text
- representation in the <code>array_</code> member, for example:</p>
-
-<pre class="cxx">
-object o;
-o.array_ = "{1,2,3}";
-db.persist (o);
-</pre>
-
- <p>Of course, <code>std::string</code> is not the most natural
- representation of an array of integers in C++. Instead,
- <code>std::vector&lt;int></code> would have been much more
- appropriate. To add support for mapping
- <code>std::vector&lt;int></code> to PostgreSQL <code>INTEGER[]</code>
- we need to provide a <code>value_traits</code> specialization
- that implements conversion between the PostgreSQL text representation
- of an array and <code>std::vector&lt;int></code>. Below is a sample
- implementation:</p>
-
-<pre class="cxx">
-namespace odb
-{
- namespace pgsql
- {
- template &lt;>
- class value_traits&lt;std::vector&lt;int>, id_string>
- {
- public:
- typedef std::vector&lt;int> value_type;
- typedef value_type query_type;
- typedef details::buffer image_type;
-
- static void
- set_value (value_type&amp; v,
- const details::buffer&amp; b,
- std::size_t n,
- bool is_null)
- {
- v.clear ();
-
- if (!is_null)
- {
- char c;
- std::istringstream is (std::string (b.data (), n));
-
- is >> c; // '{'
-
- for (c = static_cast&lt;char> (is.peek ()); c != '}'; is >> c)
- {
- v.push_back (int ());
- is >> v.back ();
- }
- }
- }
-
- static void
- set_image (details::buffer&amp; b,
- std::size_t&amp; n,
- bool&amp; is_null,
- const value_type&amp; v)
- {
- is_null = false;
- std::ostringstream os;
-
- os &lt;&lt; '{';
-
- for (value_type::const_iterator i (v.begin ()), e (v.end ());
- i != e;)
- {
- os &lt;&lt; *i;
-
- if (++i != e)
- os &lt;&lt; ',';
- }
-
- os &lt;&lt; '}';
-
- const std::string&amp; s (os.str ());
- n = s.size ();
-
- if (n > b.capacity ())
- b.capacity (n);
-
- std::memcpy (b.data (), s.c_str (), n);
- }
- };
- }
-}
-</pre>
-
- <p>Once this specialization is included in the generated code (see
- the <code>mapping</code> example in the <code>odb-examples</code>
- package for details), we can use <code>std::vector&lt;int></code>
- instead of <code>std::string</code> in our persistent class:</p>
-
-<pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("INTEGER[]")
- std::vector&lt;int> array_;
-};
-</pre>
-
- <p>If we wanted to always map <code>std::vector&lt;int></code>
- to PostgreSQL <code>INTEGER[]</code>, then we could instead
- write:</p>
-
-<pre class="cxx">
-typedef std::vector&lt;int> int_vector;
-#pragma db value(int_vector) type("INTEGER[]")
-
-#pragma db object
-class object
-{
- ...
-
- std::vector&lt;int> array_; // Mapped to INTEGER[].
-};
-</pre>
-
- <p>While the above example only shows how to handle PostgreSQL arrays,
- other types in PostgreSQL and in other databases can be supported
- in a similar way. The <code>odb-tests</code> package contains a
- set of tests in the <code>&lt;database>/custom</code> directories that,
- for each database, shows how to provide custom mapping for some of
- the extended types.</p>
-
- <h2><a name="14.9">14.9 C++ Compiler Warnings</a></h2>
-
- <p>When a C++ header file defining persistent classes and containing
- ODB pragmas is used to build the application, the C++ compiler may
- issue warnings about pragmas that it doesn't recognize. There
- are several ways to deal with this problem. The easiest is to
- disable such warnings using one of the compiler-specific command
- line options or warning control pragmas. This method is described
- in the following sub-section for popular C++ compilers.</p>
-
- <p>There are also several C++ compiler-independent methods that we
- can employ. The first is to use the <code>PRAGMA_DB</code> macro,
- defined in <code>&lt;odb/core.hxx></code>, instead of using
- <code>#pragma&nbsp;db</code> directly. This macro expands to the
- ODB pragma when compiled with the ODB compiler and to an empty
- declaration when compiled with other compilers. The following example
- shows how we can use this macro:</p>
-
- <pre class="cxx">
-#include &lt;odb/core.hxx>
-
-PRAGMA_DB(object)
-class person
-{
- ...
-
- PRAGMA_DB(id)
- unsigned long id_;
-};
- </pre>
-
- <p>An alternative to using the <code>PRAGMA_DB</code> macro is to
- group the <code>#pragma&nbsp;db</code> directives in blocks that are
- conditionally included into compilation only when compiled with the
- ODB compiler. For example:</p>
-
- <pre class="cxx">
-class person
-{
- ...
-
- unsigned long id_;
-};
-
-#ifdef ODB_COMPILER
-# pragma db object(person)
-# pragma db member(person::id_) id
-#endif
- </pre>
-
- <p>The disadvantage of this approach is that it can quickly become
- overly verbose when positioned pragmas are used.</p>
-
- <h3><a name="14.9.1">14.9.1 GNU C++</a></h3>
-
- <p>GNU g++ does not issue warnings about unknown pragmas
- unless requested with the <code>-Wall</code> command line option.
- To disable only the unknown pragma warning, we can add the
- <code>-Wno-unknown-pragmas</code> option after <code>-Wall</code>,
- for example:</p>
-
- <pre class="terminal">
-g++ -Wall -Wno-unknown-pragmas ...
- </pre>
-
- <h3><a name="14.9.2">14.9.2 Visual C++</a></h3>
-
- <p>Microsoft Visual C++ issues an unknown pragma warning (C4068) at
- warning level 1 or higher. This means that unless we have disabled
- the warnings altogether (level 0), we will see this warning.</p>
-
- <p>To disable this warning via the compiler command line, we can add
- the <code>/wd4068</code> C++ compiler option in Visual Studio 2008
- and earlier. In Visual Studio 2010 and later there is now a special
- GUI field where we can enter warning numbers that should be disabled.
- Simply enter 4068 into this field.</p>
-
- <p>We can also disable this warning for only a specific header or
- a fragment of a header using the warning control pragma. For
- example:</p>
-
- <pre class="cxx">
-#include &lt;odb/core.hxx>
-
-#pragma warning (push)
-#pragma warning (disable:4068)
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id
- unsigned long id_;
-};
-
-#pragma warning (pop)
- </pre>
-
- <h3><a name="14.9.3">14.9.3 Sun C++</a></h3>
-
- <p>The Sun C++ compiler does not issue warnings about unknown pragmas
- unless the <code>+w</code> or <code>+w2</code> option is specified.
- To disable only the unknown pragma warning we can add the
- <code>-erroff=unknownpragma</code> option anywhere on the
- command line, for example:</p>
-
- <pre class="terminal">
-CC +w -erroff=unknownpragma ...
- </pre>
-
- <h3><a name="14.9.4">14.9.4 IBM XL C++</a></h3>
-
- <p>IBM XL C++ issues an unknown pragma warning (1540-1401) by default.
- To disable this warning we can add the <code>-qsuppress=1540-1401</code>
- command line option, for example:</p>
-
- <pre class="terminal">
-xlC -qsuppress=1540-1401 ...
- </pre>
-
- <h3><a name="14.9.5">14.9.5 HP aC++</a></h3>
-
- <p>HP aC++ (aCC) issues an unknown pragma warning (2161) by default.
- To disable this warning we can add the <code>+W2161</code>
- command line option, for example:</p>
-
- <pre class="terminal">
-aCC +W2161 ...
- </pre>
-
- <h3><a name="14.9.6">14.9.6 Clang</a></h3>
-
- <p>Clang does not issue warnings about unknown pragmas
- unless requested with the <code>-Wall</code> command line option.
- To disable only the unknown pragma warning, we can add the
- <code>-Wno-unknown-pragmas</code> option after <code>-Wall</code>,
- for example:</p>
-
- <pre class="terminal">
-clang++ -Wall -Wno-unknown-pragmas ...
- </pre>
-
- <p>We can also disable this warning for only a specific header or
- a fragment of a header using the warning control pragma. For
- example:</p>
-
- <pre class="cxx">
-#include &lt;odb/core.hxx>
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunknown-pragmas"
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id
- unsigned long id_;
-};
-
-#pragma clang diagnostic pop
- </pre>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="15">15 Advanced Techniques and Mechanisms</a></h1>
-
- <p>This chapter covers more advanced techniques and mechanisms
- provided by ODB that may be useful in certain situations.</p>
-
- <h2><a name="15.1">15.1 Transaction Callbacks</a></h2>
-
- <p>The ODB transaction class (<code>odb::transaction</code>) allows
- an application to register a callback that will be called after
- the transaction is finalized, that is, committed or rolled back.
- This mechanism can be used, for example, to restore values that
- were updated during the transaction execution to their original
- states if the transaction is rolled back.</p>
-
- <p>The callback management interface of the <code>transaction</code>
- class is shown below.</p>
-
- <pre class="cxx">
-namespace odb
-{
- class transaction
- {
- ...
-
- public:
- static const unsigned short event_commit = 0x01;
- static const unsigned short event_rollback = 0x02;
- static const unsigned short event_all = event_commit | event_rollback;
-
- typedef void (*callback_type) (
- unsigned short event, void* key, unsigned long long data);
-
- void
- callback_register (callback_type callback,
- void* key,
- unsigned short event = event_all,
- unsigned long long data = 0,
- transaction** state = 0);
-
-
- void
- callback_unregister (void* key);
-
- void
- callback_update (void* key,
- unsigned short event,
- unsigned long long data = 0,
- transaction** state = 0);
- }
-}
- </pre>
-
- <p>The <code>callback_register()</code> function registers a
- post-commit/rollback callback. The <code>callback</code>
- argument is the function that should be called. The
- <code>key</code> argument is used by the transaction
- to identify this callback. It is also normally used
- to pass an address of the data object on which the
- callback function will work. The <code>event</code>
- argument is the bitwise-or of the events that should
- trigger the callback.</p>
-
- <p>The optional data argument can be used to store any POD
- user data that doesn't exceed 8 bytes in size and doesn't require
- alignment greater than <code>unsigned long long</code>. For
- example, we could store an old value of a flag or a counter
- that needs to be restored in case of a roll back.</p>
-
- <p>The optional <code>state</code> argument can be used to
- indicate that the callback has been unregistered because
- the transaction was finalized. In this case the transaction
- automatically resets the passed pointer to 0. This is
- primarily useful if we are interested in only one of
- the events (commit or rollback).</p>
-
- <p>The <code>callback_unregister()</code> function unregisters a previously
- registered callback. If the number of registered callbacks is
- large, then this can be a slow operation. Generally, the callback
- mechanism is optimized for cases where the callbacks stay
- registered until the transaction is finalized.</p>
-
- <p>Note also that you don't need to unregister a callback that has
- been called or auto-reset using the <code>state</code> argument
- passed to <code>callback_register()</code>. This function does nothing
- if the key is not found.</p>
-
- <p>The <code>callback_update()</code> function can be used to update
- the <code>event</code>, <code>data</code>, and <code>state</code>
- values of a previously registered callback. Similar to
- <code>callback_unregister()</code>, this is a potentially slow
- operation.</p>
-
- <p>When the callback is called, it is passed the event that
- triggered it, as well as the <code>key</code> and
- <code>data</code> values that were passed to the
- <code>callback_register()</code> function. Note also that the order
- in which the callbacks are called is unspecified. The rollback
- event can be triggered by an exception. In this case, if the
- callback throws, the program will be terminated.</p>
-
- <p>The following example shows how we can use transaction
- callbacks together with database operation callbacks
- (<a href="#14.1.7">Section 14.1.7, "<code>callback</code>"</a>)
- to manage the object's "dirty" flag.</p>
-
- <pre class="cxx">
-#include &lt;odb/callback.hxx>
-#include &lt;odb/transaction.hxx>
-
-#pragma db object callback(update)
-class object
-{
- ...
-
- #pragma db transient
- mutable bool dirty_;
-
- // Non-NULL value indicates that we are registered
- // with this transaction.
- //
- #pragma db transient
- mutable odb::transaction* tran_;
-
- void
- update (odb::callback_event e, odb::database&amp;) const
- {
- using namespace odb::core;
-
- if (e == callback_event::post_update)
- return;
-
- // Mark the object as clean again but register a
- // transaction callback in case the update is rolled
- // back.
- //
- tran_ = &amp;transaction::current ();
- tran_->callback_register (&amp;rollback,
- const_cast&lt;object*> (this),
- transaction::event_rollback,
- 0,
- &amp;tran_);
- dirty_ = false;
- }
-
- static void
- rollback (unsigned short, void* key, unsigned long long)
- {
- // Restore the dirty flag since the changes have been
- // rolled back.
- //
- object&amp; o (*static_cast&lt;object*> (key));
- o.dirty_ = true;
- }
-
- ~object ()
- {
- // Unregister the callback if we are going away before
- // the transaction.
- //
- if (tran_ != 0)
- tran_->callback_unregister (this);
- }
-};
- </pre>
-
- <h2><a name="15.2">15.2 Persistent Class Template Instantiations</a></h2>
-
- <p>Similar to composite value types (<a href="#7.2">Section 7.2, "Composite
- Value Types"</a>), a persistent object can be defined as an instantiation
- of a C++ class template, for example:</p>
-
- <pre class="cxx">
-template &lt;typename T>
-class person
-{
- ...
-
- T first_;
- T last_;
-};
-
-typedef person&lt;std::string> std_person;
-
-#pragma db object(std_person)
-#pragma db member(std_person::last_) id
- </pre>
-
- <p>Note that the database support code for such a persistent object
- is generated when compiling the header containing the
- <code>db&nbsp;object</code> pragma and not the header containing
- the template definition or the <code>typedef</code> name. This
- allows us to use templates defined in other files, for example:</p>
-
- <pre class="cxx">
-#include &lt;utility> // std::pair
-
-typedef std::pair&lt;unsigned int, std::string> person;
-#pragma db object(person)
-#pragma db member(person::first) id auto column("id")
-#pragma db member(person::second) column("name")
- </pre>
-
- <p>You may also have to explicitly specify the object type in
- calls to certain <code>database</code> class functions due
- to the inability do distinguish, at the API level, between
- smart pointers and persistent objects defined as class
- template instantiations. For example:</p>
-
- <pre class="cxx">
-person p;
-
-db.update (p); // Error.
-db.reload (p); // Error.
-db.erase (p); // Error.
-
-db.update&lt;person> (p); // Ok.
-db.reload&lt;person> (p); // Ok.
-db.erase&lt;person> (p); // Ok.
- </pre>
-
- <p>It also makes sense to factor persistent data members that do not
- depend on template arguments into a common, non-template base class.
- The following more realistic example illustrates this approach:</p>
-
- <pre class="cxx">
-#pragma db object abstract
-class base_common
-{
- ...
-
- #pragma db id auto
- unsigned long id;
-};
-
-template &lt;typename T>
-class base: public base_common
-{
- ...
-
- T value;
-};
-
-typedef base&lt;std::string> string_base;
-#pragma db object(string_base) abstract
-
-#pragma db object
-class derived: public string_base
-{
- ...
-};
- </pre>
-
- <h2><a name="15.3">15.3 Bulk Database Operations</a></h2>
-
- <p>Some database systems supported by ODB provide a mechanism, often called
- bulk or batch statement execution, that allows us to execute the same SQL
- statement on multiple sets of data at once and with a single database API
- call (or equivalent). This often results in significantly better
- performance if we need to execute the same statement for a large number
- of data sets (thousands to millions).</p>
-
- <p>ODB translates this mechanism to bulk operations which allow us to
- persist, update, or erase a range of objects in the database. Currently,
- from all the database systems supported by ODB, only Oracle, Microsoft
- SQL Server, and PostgreSQL are capable of bulk operations (but
- see <a href="#19.5.7">Section 19.5.7, "Bulk Operations Support"</a> for
- PostgreSQL limitations). There is also currently no emulation of the bulk
- API for other databases nor dynamic multi-database support. As a result,
- if you are using dynamic multi-database support, you will need to "drop
- down" to static support in order to access the bulk API. Refer
- to <a href="#16">Chapter 16, "Multi-Database Support"</a> for
- details.</p>
-
- <p>As we will discuss later in this section, bulk operations have
- complex failure semantics that is dictated by the underlying
- database API. As a result, support for bulk persist, update,
- and erase is limited to persistent classes for which these
- operations can be performed with a single database statement
- execution. In particular, bulk operations are not available
- for polymorphic objects (<a href="#8.2">Section 8.2,
- "Polymorphism Inheritance"</a>) or objects that have
- containers (inverse containers of object pointers are an
- exception). Furthermore, for objects that have sections
- (<a href="#9">Chapter 9, "Sections"</a>) the bulk update operation
- will only be available if all the sections are manually-updated.
- On the other hand, bulk operations are supported for objects
- that use optimistic concurrency (<a href="#12">Chapter 12,
- "Optimistic Concurrency"</a>) or have no object id
- (<a href="#14.1.6">Section 14.1.6, "<code>no_id</code>"</a>).</p>
-
- <p>To enable the generation of bulk operation support for a persistent
- class we use the <code>bulk</code> pragma. For example:</p>
-
- <pre class="cxx">
-#pragma db object bulk(5000)
-class person
-{
- ...
-
- #pragma db id auto
- unsigned long id;
-};
- </pre>
-
- <p>The single argument to the <code>bulk</code> pragma is the batch
- size. The batch size specifies the maximum number of data sets that
- should be handled with a single underlying statement execution (or
- equivalent). If the range that we want to perform the bulk operation on
- contains more objects than the batch size, then ODB will split this
- operation into multiple underlying statement executions (batches). To
- illustrate this point with an example, suppose we want to persist 53,000
- objects and the batch size is 5,000. ODB will then execute the statement
- 11 times, the first 10 times with 5,000 data sets each, and the last time
- with the remaining 3,000 data sets.</p>
-
- <p>The commonly used batch sizes are in the 2,000-5,000 range, though
- smaller or larger batches could provide better performance,
- depending on the situation. As a result, it is recommended to
- experiment with different batch sizes to determine the optimum
- number for a particular object and its use-cases. Note also that
- you may achieve better performance by also splitting a large bulk
- operation into multiple transactions (<a href="#3.5">Section 3.5,
- "Transactions"</a>).</p>
-
- <p>For database systems that do not support bulk operations the
- <code>bulk</code> pragma is ignored. It is also possible to
- specify different batch sizes for different database systems
- by using the database prefix, for example:</p>
-
- <pre class="cxx">
-#pragma db object mssql:bulk(3000) oracle:bulk(4000) pgsql:bulk(2000)
-class person
-{
- ...
-};
- </pre>
-
- <p>Note that while specifying the batch size at compile time might
- seem inflexible, this approach allows ODB to place internal
- arrays of the fixed batch size on the stack rather than
- allocating them in the dynamic memory. However, specifying the
- batch size at runtime may be supported in the future.</p>
-
- <p>Once the bulk support is enabled for a particular object, we can
- use the following <code>database</code> functions to perform bulk
- operations:</p>
-
- <pre class="cxx">
-template &lt;typename I>
-void
-persist (I begin, I end, bool continue_failed = true);
-
-template &lt;typename I>
-void
-update (I begin, I end, bool continue_failed = true);
-
-template &lt;typename I>
-void
-erase (I obj_begin, I obj_end, bool continue_failed = true);
-
-template &lt;typename T, typename I>
-void
-erase (I id_begin, I id_end, bool continue_failed = true);
- </pre>
-
- <p>Every bulk API function expects a range of elements, passed in
- the canonical C++ form as a pair of input iterators. In case of
- <code>persist()</code>, <code>update()</code>, and the first
- <code>erase()</code> overload, we pass a range of objects,
- either as references or as pointers, raw or smart. The following
- example illustrates the most common scenarios using the
- <code>persist()</code> call:</p>
-
- <pre class="cxx">
-// C array of objects.
-//
-person a[2] {{"John", "Doe"}, {"Jane", "Doe"}};
-
-db.persist (a, a + sizeof(a) / sizeof(a[0]));
-
-
-// Vector of objects.
-//
-std::vector&lt;person> v {{"John", "Doe"}, {"Jane", "Doe"}};
-
-db.persist (v.begin (), v.end ());
-
-
-// C array of raw pointers to objects.
-//
-person p1 ("John", "Doe");
-person p2 ("Jane", "Doe");
-person* pa[2] {&amp;p1, &amp;p2};
-
-db.persist (pa, pa + sizeof(pa) / sizeof(pa[0]));
-
-
-// Vector of raw pointers to objects.
-//
-std::vector&lt;person*> pv {&amp;p1, &amp;p2};
-
-db.persist (pv.begin (), pv.end ());
-
-
-// Vector of smart (shared) pointers to objects.
-//
-std::vector&lt;std::shared_ptr&lt;person>> sv {
- std::make_shared&lt;person> ("John", "Doe"),
- std::make_shared&lt;person> ("Jane", "Doe")};
-
-db.persist (sv.begin (), sv.end ());
- </pre>
-
- <p>The ability to perform a bulk operation on a range of raw pointers
- to objects can be especially useful when the application stores
- objects in a way that does not easily conform to the pair of
- iterators interface. In such cases we can create a temporary
- container of shallow pointers to objects and use that to perform
- the bulk operation, for example:</p>
-
- <pre class="cxx">
-struct person_entry
-{
- person obj;
-
- // Some additional data.
- ...
-};
-
-typedef std::vector&lt;person_entry> people;
-
-void
-persist (odb::database&amp; db, people&amp; p)
-{
- std::vector&lt;person*> tmp;
- tmp.reserve (p.size ());
- std::for_each (p.begin (),
- p.end (),
- [&amp;tmp] (person_entry&amp; pe)
- {
- tmp.push_back (&amp;pe.obj);
- });
-
-
- db.persist (tmp.begin (), tmp.end ());
-}
- </pre>
-
- <p>The second overload of the bulk <code>erase()</code> function
- allows us to pass a range of object ids rather than objects
- themselves. As with the corresponding non-bulk version, we
- have to specify the object type explicitly, for example:</p>
-
- <pre class="cxx">
-std::vector&lt;unsigned long> ids {1, 2};
-
-db.erase&lt;person> (ids.begin (), ids.end ());
- </pre>
-
- <p>Conceptually, a bulk operation is equivalent to performing the
- corresponding non-bulk version in a loop, except when it comes to the
- failure semantics. Some databases that currently are capable of bulk
- operations (specifically, Oracle and SQL Server) do not stop when a data
- set in a batch fails (for example, because of a unique constraint
- violation). Instead, they continue executing subsequent data sets until
- every element in the batch has been attempted. The
- <code>continue_failed</code> argument in the bulk functions listed
- above specifies whether ODB should extend this behavior and continue
- with subsequent batches if the one it has tried to execute has failed
- elements. The default behavior is to continue.</p>
-
- <p>The consequence of this failure semantics is that we may have
- multiple elements in the range failed for different reasons.
- For example, if we tried to persist a number of objects, some
- of them might have failed because they are already persistent
- while others &mdash; because of a unique constraint violation.
- As a result, ODB uses the special <code>odb::multiple_exceptions</code>
- class to report failures in the bulk API functions. This
- exception is thrown if one or more elements in the range have
- failed and it contains the error information in the form of other
- ODB exception for each failed position. The
- <code>multiple_exceptions</code> class has the following interface:</p>
-
- <pre class="cxx">
-struct multiple_exceptions: odb::exception
-{
- // Element type.
- //
- struct value_type
- {
- std::size_t
- position () const;
-
- const odb::exception&amp;
- exception () const;
-
- bool
- maybe () const;
- };
-
- // Iteration.
- //
- typedef std::set&lt;value_type> set_type;
-
- typedef set_type::const_iterator iterator;
- typedef set_type::const_iterator const_iterator;
-
- iterator
- begin () const;
-
- iterator
- end () const;
-
- // Lookup.
- //
- const value_type*
- operator[] (std::size_t) const;
-
- // Severity, failed and attempted counts.
- //
- std::size_t
- attempted () const;
-
- std::size_t
- failed () const;
-
- bool
- fatal () const;
-
- void
- fatal (bool);
-
- // Direct data access.
- //
- const set_type&amp;
- set () const;
-
- // odb::exception interface.
- //
- virtual const char*
- what () const throw ();
-};
- </pre>
-
- <p>The <code>multiple_exceptions</code> class has a map-like interface
- with the key being the position in the range and the value being
- the exception plus the <code>maybe</code> flag (discussed below).
- As a result, we can either iterate over the failed positions or
- we can check whether a specific position in the range has failed.
- The following example shows what a <code>catch</code>-handler for
- this exception might look like:</p>
-
- <pre class="cxx">
-std::vector&lt;person> objs {{"John", "Doe"}, {"Jane", "Doe"}};
-
-try
-{
- db.persist (objs.begin (), objs.end ());
-}
-catch (const odb::multiple_exceptions&amp; me)
-{
- for (const auto&amp; v: me)
- {
- size_t p (v.position ());
-
- try
- {
- throw v.exception ();
- }
- catch (const odb::object_already_persistent&amp;)
- {
- cerr &lt;&lt; p &lt;&lt; ": duplicate id: " &lt;&lt; objs[p].id () &lt;&lt; endl;
- }
- catch (const odb::exception&amp; e)
- {
- cerr &lt;&lt; p &lt;&lt; ": " &lt;&lt; e.what () &lt;&lt; endl;
- }
- }
-}
- </pre>
-
- <p>If, however, all we want is to show the diagnostics to the user,
- then the string returned by the <code>what()</code> function
- will contain the error information for each failed position.
- Here is what it might look like (using Oracle as an example):</p>
-
- <pre class="terminal">
-multiple exceptions, 4 elements attempted, 2 failed:
-[0] object already persistent
-[3] 1: ORA-00001: unique constraint (ODB_TEST.person_last_i) violated
- </pre>
-
- <p>Some databases that currently are capable of bulk operations
- (specifically, Oracle and SQL Server) return a total count of affected
- rows rather than individual counts for each data set. This limitation
- prevents ODB from being able to always determine which elements in the
- batch haven't affected any rows and, for the update and erase operations,
- translate this to the <code>object_not_persistent</code> exceptions. As a
- result, if some elements in the batch haven't affected any rows and ODB
- is unable to determine exactly which ones, it will mark all the elements
- in this batch as "maybe not persistent". That is, it will insert
- the <code>object_not_persistent</code> exception and set
- the <code>maybe</code> flag for every position in the batch. The
- diagnostics string returned by <code>what()</code> will also reflect this
- situation, for example (assuming batch size of 3):</p>
-
- <pre class="terminal">
-multiple exceptions, 4 elements attempted, 4 failed:
-[0-2] (some) object not persistent
-[3] object not persistent
- </pre>
-
- <p>The way to handle and recover from such "maybe failures" will have
- to be application-specific. For example, for some applications the
- fact that some objects no longer exist in the database when
- performing bulk erase might be an ignorable error. If, however,
- the application needs to determine exactly which elements in the batch
- have failed, then a <code>load()</code> call will be required for each
- element in the batch (or a query using a view to avoid loading all
- the data members; <a href="#10">Chapter 10, "Views"</a>). This is also
- something to keep in mind when selecting the batch size since for
- larger sizes it will be more expensive (more loads to perform) to
- handle such "maybe failures". If the failures are not uncommon, as
- is the case, for example, when using optimistic concurrency, then
- it may make sense to use a smaller batch.</p>
-
- <p>The lookup operator (<code>operator[]</code>) returns <code>NULL</code>
- if the element at this position has no exception. Note also that the
- returned value is <code>value_type*</code> and not
- <code>odb::exception*</code> in order to provide access to the
- <code>maybe</code> flag discussed above.</p>
-
- <p>The <code>multiple_exceptions</code> class also provides access
- to the number of positions attempted (the <code>attempted()</code>
- accessor) and failed (the <code>failed()</code> accessor). Note
- that the failed count includes the "maybe failed" positions.</p>
-
- <p>The <code>multiple_exceptions</code> exception can also be fatal.
- If the <code>fatal()</code> accessor returns <code>true</code>, then
- (some of) the exceptions were fatal. In this case, even for positions
- that did not fail, no attempts were made to complete the operation
- and the transaction must be aborted.</p>
-
- <p>If <code>fatal()</code> returns false, then the operation on the
- elements that don't have an exception has succeeded. The application
- can ignore the errors or try to correct the errors and re-attempt
- the operation on the elements that did fail. In either case, the
- transaction can be committed.</p>
-
- <p>An example of a fatal exception would be the situation where the
- execution of the underlying statement failed summarily, without
- attempting any data sets, for instance, because of an error in
- the statement itself.</p>
-
- <p>The <code>fatal()</code> modifier allows you to "upgrade" an
- exception to fatal, for example, for specific database error
- codes.</p>
-
-
- <!-- PART -->
-
-
- <hr class="page-break"/>
- <h1><a name="II">PART II&nbsp;&nbsp;
- <span style="font-weight: normal;">DATABASE SYSTEMS</span></a></h1>
-
- <p>Part II covers topics specific to the database system
- implementations and their support in ODB. The first chapter in
- Part II discusses how to use multiple database systems in the
- same application. The subsequent chapters describe the system-specific
- <code>database</code> classes as well as the default mapping
- between basic C++ value types and native database types. Part
- II consists of the following chapters.</p>
-
- <table class="toc">
- <tr><th>16</th><td><a href="#16">Multi-Database Support</a></td></tr>
- <tr><th>17</th><td><a href="#17">MySQL Database</a></td></tr>
- <tr><th>18</th><td><a href="#18">SQLite Database</a></td></tr>
- <tr><th>19</th><td><a href="#19">PostgreSQL Database</a></td></tr>
- <tr><th>20</th><td><a href="#20">Oracle Database</a></td></tr>
- <tr><th>21</th><td><a href="#21">Microsoft SQL Server Database</a></td></tr>
- </table>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="16">16 Multi-Database Support</a></h1>
-
- <p>Some applications may need to access multiple database systems, either
- simultaneously or one at a time. For example, an application may
- utilize an embedded database such as SQLite as a local cache and use
- a client-server database such as PostgreSQL for more permanent
- but slower to access remote storage. Or an application may need
- to be able to store its data in any database selected at runtime
- by the user. Yet another scenario is the data migration from one
- database system to another. In this case, multi-database support
- is only required for a short period. It is also plausible that an
- application implements all three of these scenarios, that is, it
- uses SQLite as a local cache, allows the user to select the remote
- database system, and supports data migration from one remote database
- system to another.</p>
-
- <p>ODB provides two types of multi-database support: <em>static</em>
- and <em>dynamic</em>. With static support we use the
- database system-specific interfaces to perform database
- operations. That is, instead of using <code>odb::database</code>,
- <code>odb::transaction</code>, or <code>odb::query</code>, we
- would use, for example, <code>odb::sqlite::database</code>,
- <code>odb::sqlite::transaction</code>, or
- <code>odb::sqlite::query</code> to access an SQLite database.</p>
-
- <p>In contrast, with <em>dynamic</em> multi-database support we can
- use the common interface to access any database without having to
- know which one it is. At runtime, ODB will automatically dispatch
- a call on the common interface to the specific database implementation
- based on the actual <code>database</code> instance being
- used. In fact, this mechanism is very similar to C++ virtual
- functions.</p>
-
- <p>Both static and dynamic multi-database support have a different set
- of advantages and disadvantages which makes them more or less suitable
- for different use cases. Static support has zero overhead compared
- to single-database support and allows us to use database
- system-specific features, extensions, etc. At the same time, the
- code that we write will be tied to the specific database system.
- As a result, this type of multi-database support is more
- suitable for situations where different parts of an application
- access different but specific database systems. For example,
- using SQLite as a local cache most likely falls into this
- category since we are using a specific database system (SQLite)
- and the code that will check the cache will most likely (but
- not necessarily) be separate from the code that interact with
- the remote database. Another example where static multi-database
- support might be more suitable is a once-off data migration from
- one database system to another. In this case both the source and
- target are specific database systems. In contrast, if data migration
- from one database system to another is a general feature in an
- application, then dynamic multi-database support might be more
- suitable.</p>
-
- <p>The main advantage of dynamic multi-database support is the
- database system-independence of the code that we write. The same
- application code will work with any database system supported by
- ODB and the generated database support code can be packaged into
- separate libraries and loaded dynamically by the application. The
- disadvantages of dynamic support are slight overhead and certain
- limitations in functionality compared to static support (see
- <a href="#16.2">Section 16.2, "Dynamic Multi-Database Support"</a>
- for details). As a result, dynamic multi-database support is most
- suitable to situations where we need the same code to
- work with a range of database systems. For example, if your
- application must be able to store its data in any database
- selected by the user, then dynamic support is probably the
- best option.</p>
-
- <p>Note also that it is possible to mix and match static and dynamic
- support in the same application. In fact, dynamic support is built
- on top of static support so it is possible to use the same database
- system both "statically" and "dynamically". In particular, the ability
- to "drop down" from dynamic to static support can be used to overcome
- the functionality limitations mentioned above. Finally,
- single-database support is just a special case of static
- multi-database support with a single database system.</p>
-
- <p>By default ODB assumes single-database support. To enable
- multi-database support we use the <code>--multi-database</code>
- (or <code>-m</code>) ODB compiler option. This option is also used to
- specify the support type: <code>static</code> or <code>dynamic</code>.
- For example:</p>
-
- <pre class="terminal">
-odb -m static ... person.hxx
- </pre>
-
- <p>With multi-database support enabled, we can now generate the database
- support code for several database systems. This can be accomplished
- either with a single ODB compiler invocation by specifying multiple
- <code>--database</code> (or <code>-d</code>) options or with multiple
- ODB compiler invocations. Both approaches produce the same result,
- for example:</p>
-
- <pre class="terminal">
-odb -m static -d common -d sqlite -d pgsql person.hxx
- </pre>
-
- <p>Is equivalent to:</p>
-
- <pre class="terminal">
-odb -m static -d common person.hxx
-odb -m static -d sqlite person.hxx
-odb -m static -d pgsql person.hxx
- </pre>
-
- <p>Notice that the first <code>-d</code> option has <code>common</code>
- as its value. This is not a real database system. Rather, it instructs
- the ODB compiler to generate code that is common to all the database
- systems and, in case of dynamic support, is also the common
- interfaces.</p>
-
- <p>If you look at the result of the above commands, you will also notice
- changes in the output file names. In the single-database mode the ODB
- compiler produces a single set of the <code>person-odb.?xx</code> files
- which contain both the common as well as the database specific
- generated code (since there is only one database system in use,
- there is no reason to split the two). In contrast, in the
- multi-database mode, the <code>person-odb.?xx</code> set of files
- contains the common code while the database system-specific code is
- written to files in the form <code>person-odb-&lt;db>.?xx</code>.
- That is, <code>person-odb-sqlite.?xx</code> for SQLite,
- <code>person-odb-pgsql.?xx</code> for PostgreSQL, etc.</p>
-
- <p>If we need dynamic support for some databases and static for
- others, then the <code>common</code> code must be generated
- in the dynamic mode. For example, if we need static support
- for SQLite and dynamic support for PostgreSQL and Oracle, then
- the ODB compiler invocations could look like this:</p>
-
- <pre class="terminal">
-odb -m dynamic -d common person.hxx
-odb -m static -d sqlite person.hxx
-odb -m dynamic -d pgsql person.hxx
-odb -m dynamic -d oracle person.hxx
- </pre>
-
- <p>With multi-database support enabled, it is possible to restrict ODB
- pragmas to apply only to a specific database system (unrestricted
- pragmas apply to all the databases). For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db pgsql:type("VARCHAR(128)") sqlite:type("TEXT")
- std::string name_;
-
- unsigned short age_;
-
- #pragma db pgsql index member(age_)
-};
- </pre>
-
- <p>Above, the pragma for the <code>name_</code> data member shows the
- use of a database prefix (for example, <code>pgsql:</code>) that
- only applies to the specifier that follows. The pragma that defines
- an index on the <code>age_</code> data member shows the use of a
- database prefix that applies to the whole pragma. In this case the
- database name must immediately follow the <code>db</code> keyword.</p>
-
-
- <p>Similar to pragmas, ODB compiler options that determine the kind
- (for example, <code>--schema-format</code>), names (for example,
- <code>--odb-file-suffix</code>), or content (for example, prologue
- and epilogue options) of the output files can be prefixed with the
- database name. For example:</p>
-
- <pre class="terminal">
-odb --odb-file-suffix common:-odb-common ...
- </pre>
-
- <p>Dynamic multi-database support requires consistent mapping across
- all the databases. That is, the same classes and data members
- should be mapped to objects, simple/composite values, etc., for
- all the databases. In contrast, static multi-database support
- does not have this restriction. Specifically, with static support,
- some data members can be transient for some database systems.
- Similarly, the same class (for example, <code>point</code>) can
- be mapped to a simple value in one database (for example, to the
- <code>POINT</code> PostgreSQL type) and to a composite value
- in another (for example, in SQLite, which does not have a
- built-in point type).</p>
-
- <p>The following sections discuss static and dynamic multi-database
- support in more detail.</p>
-
-
- <h2><a name="16.1">16.1 Static Multi-Database Support</a></h2>
-
- <p>With static multi-database support, instead of including
- <code>person-odb.hxx</code>, application source code has
- to include <code>person-odb-&lt;db>.hxx</code> header files
- corresponding to the database systems that will be used.</p>
-
- <p>The application code has to also use database system-specific
- interfaces when performing database operations. As an example,
- consider the following transaction in a single-database
- application. It uses the common interfaces, that is, classes
- from the <code>odb</code> namespace.</p>
-
- <pre class="cxx">
-#include "person-odb.hxx"
-
-odb::database&amp; db = ...
-
-typedef odb::query&lt;person> query;
-typedef odb::result&lt;person> result;
-
-odb::transaction t (db.begin ());
-result r (db.query&lt;person> (query::age &lt; 30));
-...
-t.commit ();
- </pre>
-
- <p>In an application that employs static multi-database support
- the same transaction for SQLite would be rewritten like this:</p>
-
- <pre class="cxx">
-#include "person-odb-sqlite.hxx"
-
-odb::sqlite::database&amp; db = ...
-
-typedef odb::sqlite::query&lt;person> query;
-typedef odb::result&lt;person> result; // odb:: not odb::sqlite::
-
-odb::sqlite::transaction t (db.begin ());
-result r (db.query&lt;person> (query::age &lt; 30));
-...
-t.commit ();
- </pre>
-
- <p>That is, the <code>database</code>, <code>transaction</code>, and
- <code>query</code> classes now come from the <code>odb::sqlite</code>
- namespace instead of <code>odb</code>. Other classes that have
- database system-specific interfaces are <code>connection</code>,
- <code>statement</code>, and <code>tracer</code>. Note that
- all of them derive from the corresponding common versions. It
- is also possible to use common <code>transaction</code>,
- <code>connection</code>, and <code>statement</code> classes
- with static support, if desired.</p>
-
- <p>Notice that we didn't use the <code>odb::sqlite</code> namespace
- for the <code>result</code> class template. This is because
- <code>result</code> is database system-independent. All other
- classes defined in namespace <code>odb</code>, except those
- specifically mentioned above, are database system-independent.
- In particular, <code>result</code>, <code>prepared_query</code>,
- <code>session</code>, <code>schema_catalog</code>, and all the
- exceptions are database system-independent.</p>
-
- <p>Writing <code>odb::sqlite::</code> before every name can quickly
- become burdensome. As we have seen before, in single-database
- applications that use the common interface we can add the
- <code>using namespace</code> directive to avoid qualifying
- each name. For example:</p>
-
- <pre class="cxx">
-#include "person-odb.hxx"
-
-odb::database&amp; db = ...
-
-{
- using namespace odb::core;
-
- typedef query&lt;person> person_query;
- typedef result&lt;person> person_result;
-
- transaction t (db.begin ());
- person_result r (db.query&lt;person> (person_query::age &lt; 30));
- ...
- t.commit ();
-}
- </pre>
-
- <p>A similar mechanism is available in multi-database support. Each
- database runtime defines the <code>odb::&lt;db>::core</code>
- namespace that contains all the database system-independent
- names as well as the database system-specific ones for this
- database. Here is how we can rewire the above transaction
- using this approach:</p>
-
- <pre class="cxx">
-#include "person-odb-sqlite.hxx"
-
-odb::sqlite::database&amp; db = ...
-
-{
- using namespace odb::sqlite::core;
-
- typedef query&lt;person> person_query;
- typedef result&lt;person> person_result;
-
- transaction t (db.begin ());
- person_result r (db.query&lt;person> (person_query::age &lt; 30));
- ...
- t.commit ();
-}
- </pre>
-
- <p>If the <code>using namespace</code> directive cannot be used, for
- example, because the same code fragment accesses several databases,
- then we can still make the namespace qualifications more concise
- by assigning shorter aliases to database namespaces. For example:</p>
-
- <pre class="cxx">
-#include "person-odb-pgsql.hxx"
-#include "person-odb-sqlite.hxx"
-
-namespace pg = odb::pgsql;
-namespace sl = odb::sqlite;
-
-pg::database&amp; pg_db = ...
-sl::database&amp; sl_db = ...
-
-typedef pg::query&lt;person> pg_query;
-typedef sl::query&lt;person> sl_query;
-typedef odb::result&lt;person> result;
-
-// First check the local cache.
-//
-odb::transaction t (sl_db.begin ()); // Note: using common transaction.
-result r (sl_db.query&lt;person> (sl_query::age &lt; 30));
-
-// If no hits, try the remote database.
-//
-if (r.empty ())
-{
- t.commit (); // End the SQLite transaction.
- t.reset (pg_db.begin ()); // Start the PostgreSQL transaction.
-
- r = pg_db.query&lt;person> (pg_query::age &lt; 30);
-}
-
-// Handle the result.
-//
-...
-
-t.commit ();
- </pre>
-
- <p>With static multi-database support we can make one of the databases
- the default database with the <code>--default-database</code> option.
- The default database can be accessed via the common interface, just
- like with single-database support. For example:</p>
-
- <pre class="terminal">
-odb -m static -d common -d pgsql -d sqlite --default-database pgsql ...
- </pre>
-
- <p>The default database mechanism can be useful when one of the
- databases is primary or when retrofitting multi-database support
- into an existing single-database application. For example, if
- we are adding SQLite as a local cache into an existing
- application that uses PostgreSQL as its only database, then
- by making PostgreSQL the default database we avoid having to
- change all the existing code. Note that if dynamic multi-database
- support is enabled, then the common (dynamic) interface is always
- made the default database.</p>
-
- <h2><a name="16.2">16.2 Dynamic Multi-Database Support</a></h2>
-
- <p>With dynamic multi-database support, application source code only
- needs to include the <code>person-odb.hxx</code> header file, just
- like with single-database support. In particular, we don't need
- to include any of the <code>person-odb-&lt;db>.hxx</code> files
- unless we would also like to use certain database systems in the
- static multi-database mode.</p>
-
- <p>When performing database operations, the application code
- uses the common interfaces from the <code>odb</code> namespace,
- just like with single-database support. As an example, consider
- a function that can be used to load an object either from a local
- SQLite cache or a remote PostgreSQL database (in reality, this
- function can be used with any database system support by ODB
- provided we generated the database support code for this database
- and linked it into our application):</p>
-
- <pre class="cxx">
-#include "person-odb.hxx"
-
-std::unique_ptr&lt;person>
-load (odb::database&amp; db, const std::string&amp; name)
-{
- odb::transaction t (db.begin ());
- std::unique_ptr&lt;person> p (db.find (name));
- t.commit ();
- return p;
-}
-
-odb::pgsql::database&amp; pg_db = ...
-odb::sqlite::database&amp; sl_db = ...
-
-// First try the local cache.
-//
-std::unique_ptr&lt;person> p (load (sl_db, "John Doe"));
-
-// If not found, try the remote database.
-//
-if (p == 0)
- p = load (pg_db, "John Doe");
-
-...
- </pre>
-
- <p>As you can see, we can use dynamic multi-database support just like
- single-database support except that now our code can work with
- different database systems. Note, however, one difference: with
- single-database support we could perform database operations using
- either the common <code>odb::database</code> or a database system-specific
- (for example, <code>odb::sqlite::database</code>) interface
- with the same effect. In contrast, with dynamic multi-database support,
- the use of the database system-specific interface results in the
- switch to the static mode (for which, as was mentioned earlier, we would
- need to include the corresponding <code>person-odb-&lt;db>.hxx</code>
- header file). As we will discuss shortly, switching from dynamic to
- static mode can be used to overcome limitations imposed by dynamic
- multi-database support.</p>
-
- <p>Dynamic multi-database support has certain overheads and limitations
- compared to static support. For database operations, the generated code
- maintains function tables that are used to dispatch calls to the database
- system-specific implementations. In single-database and static
- multi-database support, the <code>query</code> type implements a thin
- wrapper around the underlying database system's <code>SELECT</code>
- statement. With dynamic multi-database support, because the
- underlying database system is only known at query execution
- (or preparation) time, the <code>query</code> type stores a
- database system-independent representation of the query that
- is then translated to the database system-specific form. Because
- of this database system-independent representation, dynamic
- support queries have a number of limitations. Specifically, dynamic
- queries do not support parameter binding in native query fragments.
- They also make copies of by-value parameterd (by-reference parameters
- can be used to remove this overhead). Finally, parameters of array
- types (for example, <code>char[256]</code>) can only be bound
- by-reference.</p>
-
- <p>As we mentioned earlier, switching from dynamic to static mode
- can be an effective way to overcome these limitations. As an
- example, consider a function that prints the list of people of
- a certain age. The caller also specified the limit on the number
- of entries to print. Some database systems, for example, PostgreSQL,
- allow us to propagate this limit to the database server with the
- <code>LIMIT</code> clause. To add this clause we would need to
- construct a native query fragment and, as we discussed above, we
- won't be able to bind a parameter (the limit) while in the dynamic
- mode. The following implementation shows how we can overcome this
- by switching to the static mode and using the PostgreSQL-specific
- interface:</p>
-
- <pre class="cxx">
-#include "person-odb.hxx"
-#include "person-odb-pgsql.hxx" // Needed for static mode.
-
-void
-print (odb::database&amp; db, unsigned short age, unsigned long limit)
-{
- typedef odb::query&lt;person> query;
- typedef odb::result&lt;person> result;
-
- odb::transaction t (db.begin ());
-
- query q (query::age == age);
- result r;
-
- if (db.id () == odb::id_pgsql)
- {
- // We are using PostgreSQL. Drop down to the static mode and
- // add the LIMIT clause to the query.
- //
- namespace pg = odb::pgsql;
- typedef pg::query&lt;person> pg_query;
-
- pg::database&amp; pg_db (static_cast&lt;pg::database&amp;> (db));
- pg_query pg_q (pg_query (q) + "LIMIT" + pg_query::_val (limit));
- r = pg_db.query&lt;person> (pg_q);
- }
- else
- r = db.query&lt;person> (q);
-
- // Handle the result up to the limit elements.
- //
- ...
-
- t.commit ();
-}
-
-odb::pgsql::database&amp; pg_db = ...
-odb::sqlite::database&amp; sl_db = ...
-
-print (sl_db, 30, 100);
-print (sl_db, 30, 100);
- </pre>
-
- <p>A few things to note about this example. First, we use the
- <code>database::id()</code> function to determine the actual database
- system we use. This function has the following signature:</p>
-
- <pre class="cxx">
-namespace odb
-{
- enum database_id
- {
- id_mysql,
- id_sqlite,
- id_pgsql,
- id_oracle,
- id_mssql,
- id_common
- };
-
- class database
- {
- public:
- ...
-
- database_id
- id () const;
- }
-}
- </pre>
-
- <p>Note that <code>database::id()</code> can never return the
- <code>id_common</code> value.</p>
-
- <p>The other thing to note is how we translate the dynamic query
- to the database system-specific one (the <code>pg_query (q)</code>
- expression). Every <code>odb::&lt;db>::query</code> class provides
- such a translation constructor.</p>
-
- <h3><a name="16.2.2">16.2.2 Dynamic Loading of Database Support Code</a></h3>
-
- <p>With dynamic multi-database support, the generated database support
- code automatically registers itself with the function tables that
- we mentioned earlier. This makes it possible to package the generated
- code for each database into a separate dynamic-link library (Windows
- DLL) or dynamic shared object (Unix DSO; collectively referred to as
- DLLs from now on) and load/unload them from the application
- dynamically using APIs such as Win32 <code>LoadLibrary()</code> or
- POSIX <code>dlopen()</code>. This allows the application address
- space to contain code only for database systems that are actually
- needed in any particular moment. Another advantage of this approach
- is the ability to distribute individual database system support
- separately.</p>
-
- <p>This section provides an overview of how to package the generated
- database support code into DLLs for both Windows and Unix using
- GNU/Linux as an example. Note also that if static multi-database
- support is used for a particular database system, then the dynamic
- loading cannot be used for this database. It is, however, still
- possible to package the generated code into a DLL but this DLL
- will have to be linked to the executable at link-time rather
- than at runtime. If dynamic loading is desirable in this situation,
- then another alternative would be to package the functionality
- that requires static support together with the database support
- code into the DLL and import this functionality dynamically
- using the <code>GetProcAddress()</code> (Win32) or <code>dlsym()</code>
- (Unix) function.</p>
-
- <p>The first step in packaging the generated code into DLLs is to
- set up the symbol exporting. This step is required for
- Windows DLLs but is optional for Unix DSOs. Most modern Unix
- systems (such as GNU/Linux) provide control over symbol
- visibility, which is a mechanism similar to Windows symbol
- exporting. Notable advantages of using this mechanism to
- explicitly specify which symbols are visible include
- smaller Unix DSOs and faster load times. If, however, you are
- not planning to control symbol visibility on Unix, then you can
- skip directly to the second step below.</p>
-
- <p>An important point to understand is that we only need to export
- the common interface, that is, the classes defined in the
- <code>person-odb.hxx</code> header. In particular, we don't need
- to export the database system-specific classes defined in
- the <code>person-odb-&lt;db>.hxx</code>, unless we are also using
- this database in the static mode (in which case, the procedure
- described below will need to be repeated for that database as
- well).</p>
-
- <p>The ODB compiler provides two command line options,
- <code>--export-symbol</code> and <code>--extern-symbol</code>,
- which can be used to insert the export and extern
- macros in all the necessary places in the generated header file.
- You are probably familiar with the concept of export macro which
- expands to an export directive if we are building the DLL and to
- an import directive if we are building client code. The
- extern macro is a supplementary mechanism which is necessary to
- export explicit template instantiations used by the generated
- code when query support is enabled. As we will see shortly, the
- extern macro must expand into the <code>extern</code> C++ keyword
- in certain situations and must be left undefined in others. To
- manage all these macro definitions, it is customary to create the
- so called export header. Based on a single macro that is normally
- defined in the project file or on the command line and which
- indicates whether we are building the DLL or client code, the
- export header file sets the export and extern macros to their
- appropriate values. Continuing with our person example, on Windows
- the export header, which we will call <code>person-export.hxx</code>,
- could look like this:</p>
-
- <pre class="cxx">
-// person-export.hxx
-//
-// Define PERSON_BUILD_DLL if we are building the DLL. Leave it
-// undefined in client code.
-//
-#ifndef PERSON_EXPORT_HXX
-#define PERSON_EXPORT_HXX
-
-#ifdef PERSON_BUILD_DLL
-# define PERSON_EXPORT __declspec(dllexport)
-#else
-# define PERSON_EXPORT __declspec(dllimport)
-# define PERSON_EXTERN extern
-#endif
-
-#endif // PERSON_EXPORT_HXX
- </pre>
-
- <p>The equivalent export header for GCC on GNU/Linux is shown below.
- Note also that on GNU/Linux, by default, all symbols are visible
- and we need to add the GCC <code>-fvisibility=hidden</code> option to
- make them hidden by default.</p>
-
- <pre class="cxx">
-// person-export.hxx
-//
-#ifndef PERSON_EXPORT_HXX
-#define PERSON_EXPORT_HXX
-
-#define PERSON_EXPORT __attribute__ ((visibility ("default")))
-#define PERSON_EXTERN extern
-
-#endif // PERSON_EXPORT_HXX
- </pre>
-
- <p>Next we need to export the <code>person</code> persistent class
- using the export macro and re-compile our <code>person.hxx</code> file
- with the <code>--export-symbol</code> and <code>--extern-symbol</code>
- options. We will also need to include <code>person-export.hxx</code>
- into the generated <code>person-odb.hxx</code> file. For that we use
- the <code>--hxx-prologue</code> option. Here is how we can do
- this with multiple invocations of the ODB compiler:</p>
-
- <pre class="terminal">
-odb -m dynamic -d common --hxx-prologue "#include \"person-export.hxx\"" \
---export-symbol PERSON_EXPORT --extern-symbol PERSON_EXTERN person.hxx
-
-odb -m dynamic -d sqlite person.hxx
-odb -m dynamic -d pgsql person.hxx
- </pre>
-
- <p>It is also possible to achieve the same with a single invocation.
- Here we need to restrict some option values to apply only to the
- <code>common</code> database:</p>
-
- <pre class="terminal">
-odb -m dynamic -d common -d sqlite -d pgsql \
---hxx-prologue "common:#include \"person-export.hxx\"" \
---export-symbol common:PERSON_EXPORT --extern-symbol common:PERSON_EXTERN \
-person.hxx
- </pre>
-
- <p>The second step in packaging the generated code into DLLs is to
- decide where to place the generated common interface code. One
- option is to place it into a DLL of its own so that we will end
- up with (replace <code>*.dll</code> with <code>lib*.so</code> for
- Unix): <code>person.dll</code> plus <code>person-sqlite.dll</code> and
- <code>person-pgsql.dll</code>, which both link to <code>person.dll</code>,
- as well as <code>person.exe</code>, which links to <code>person.dll</code>
- and dynamically loads <code>person-sqlite.dll</code>
- and/or <code>person-pgsql.dll</code>. If this is the organization
- that you prefer, then the next step is to build all the DLLs as you
- normally would any other DLL, placing <code>person-odb.cxx</code>
- and <code>person.cxx</code> into <code>person.dll</code>,
- <code>person-odb-sqlite.cxx</code> into <code>person-sqlite.dll</code>,
- etc. Note that in the pure dynamic multi-database support,
- <code>person-sqlite.dll</code> and <code>person-pgsql.dll</code>
- do not export any symbols.</p>
-
- <p>We can improve on the above organization by getting rid of
- <code>person.dll</code>, which is not really necessary unless
- we have multiple executables sharing the same database support.
- To achieve this, we will place <code>person-odb.cxx</code> into
- <code>person.exe</code> and export its symbols from the executable
- instead of a DLL. Exporting symbols from an executable is a seldom
- used functionality, especially on Windows, however, it is well
- supported on both Windows and most Unix platforms. Note also that
- this approach won't work if we also use one of the databases in the
- static mode.</p>
-
- <p>On Windows all we have to do is place <code>person-odb.cxx</code>
- into the executable and compile it as we would in a DLL (that is,
- with the <code>PERSON_BUILD_DLL</code> macro defined). If Windows
- linker detects that an executable exports any symbols, then it
- will automatically create the corresponding import library
- (<code>person.lib</code> in our case). We then use this import
- library to build <code>person-sqlite.dll</code> and
- <code>person-pgsql.dll</code> as before.</p>
-
- <p>To export symbols from an executable on GNU/Linux all we need to
- do is add the <code>-rdynamic</code> option when linking our
- executable.</p>
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="17">17 MySQL Database</a></h1>
-
- <p>To generate support code for the MySQL database you will need
- to pass the "<code>--database&nbsp;mysql</code>"
- (or "<code>-d&nbsp;mysql</code>") option to the ODB compiler.
- Your application will also need to link to the MySQL ODB runtime
- library (<code>libodb-mysql</code>). All MySQL-specific ODB
- classes are defined in the <code>odb::mysql</code> namespace.</p>
-
- <h2><a name="17.1">17.1 MySQL Type Mapping</a></h2>
-
- <p>The following table summarizes the default mapping between basic
- C++ value types and MySQL database types. This mapping can be
- customized on the per-type and per-member basis using the ODB
- Pragma Language (<a href="#14">Chapter 14, "ODB Pragma
- Language"</a>).</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>C++ Type</th>
- <th>MySQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>bool</code></td>
- <td><code>TINYINT(1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char</code></td>
- <td><code>CHAR(1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>signed char</code></td>
- <td><code>TINYINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned char</code></td>
- <td><code>TINYINT UNSIGNED</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>short</code></td>
- <td><code>SMALLINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned short</code></td>
- <td><code>SMALLINT UNSIGNED</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>int</code></td>
- <td><code>INT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned int</code></td>
- <td><code>INT UNSIGNED</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long</code></td>
- <td><code>BIGINT UNSIGNED</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long long</code></td>
- <td><code>BIGINT UNSIGNED</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>float</code></td>
- <td><code>FLOAT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>double</code></td>
- <td><code>DOUBLE</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>std::string</code></td>
- <td><code>TEXT/VARCHAR(128)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char[N]</code></td>
- <td><code>VARCHAR(N-1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
- </table>
-
- <p>It is possible to map the <code>char</code> C++ type to an integer
- database type (for example, <code>TINYINT</code>) using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>).</p>
-
- <p>Note that the <code>std::string</code> type is mapped
- differently depending on whether a member of this type
- is an object id or not. If the member is an object id,
- then for this member <code>std::string</code> is mapped
- to the <code>VARCHAR(128)</code> MySQL type. Otherwise,
- it is mapped to <code>TEXT</code>.</p>
-
- <p>Additionally, by default, C++ enums and C++11 enum classes are
- automatically mapped to suitable MySQL types. Contiguous
- enumerations with the zero first enumerator are mapped to
- the MySQL <code>ENUM</code> type. All other enumerations
- are mapped to the MySQL types corresponding to their
- underlying integral types (see table above). In both
- cases the default <code>NULL</code> semantics is
- <code>NOT NULL</code>. For example:</p>
-
- <pre class="cxx">
-enum color {red, green, blue};
-enum class taste: unsigned char
-{
- bitter = 1, // Non-zero first enumerator.
- sweet,
- sour = 4, // Non-contiguous.
- salty
-};
-
-#pragma db object
-class object
-{
- ...
-
- color color_; // Mapped to ENUM ('red', 'green', 'blue') NOT NULL.
- taste taste_; // Mapped to TINYNT UNSIGNED NOT NULL.
-};
- </pre>
-
- <p>The only built-in mapping provided for the MySQL <code>DECIMAL</code>
- type is to <code>std::string/char[N]</code>, for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type ("DECIMAL(6,3)")
- std::string value_;
-};
- </pre>
-
- <p>You can, however, map <code>DECIMAL</code> to a custom C++ type by
- providing a suitable <code>odb::mysql::value_traits</code>
- specialization.</p>
-
- <p>It is also possible to add support for additional MySQL types,
- such as geospatial types. For more information, refer to
- <a href="#14.8">Section 14.8, "Database Type Mapping
- Pragmas"</a>.</p>
-
- <h3><a name="17.1.1">17.1.1 String Type Mapping</a></h3>
-
- <p>The MySQL ODB runtime library provides support for mapping the
- <code>std::string</code>, <code>char[N]</code>, and
- <code>std::array&lt;char, N></code> types to the MySQL <code>CHAR</code>,
- <code>VARCHAR</code>, <code>TEXT</code>, <code>NCHAR</code>, and
- <code>NVARCHAR</code> types. However, these mappings are not enabled
- by default (in particular, by default, <code>std::array</code> will
- be treated as a container). To enable the alternative mappings for
- these types we need to specify the database type explicitly using
- the <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section
- 14.4.3, "<code>type</code>"</a>), for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("CHAR(2)")
- char state_[2];
-
- #pragma db type("VARCHAR(128)")
- std::string name_;
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-#pragma db value(std::string) type("VARCHAR(128)")
-
-#pragma db object
-class object
-{
- ...
-
- std::string name_; // Mapped to VARCHAR(128).
-};
- </pre>
-
- <p>The <code>char[N]</code> and <code>std::array&lt;char, N></code> values
- may or may not be zero-terminated. When extracting such values from the
- database, ODB will append the zero terminator if there is enough
- space.</p>
-
- <h3><a name="17.1.2">17.1.2 Binary Type Mapping</a></h3>
-
- <p>The MySQL ODB runtime library provides support for mapping the
- <code>std::vector&lt;char></code>,
- <code>std::vector&lt;unsigned&nbsp;char></code>,
- <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
- <code>std::array&lt;char, N></code>, and
- <code>std::array&lt;unsigned char, N></code>
- types to the MySQL <code>BINARY</code>, <code>VARBINARY</code>,
- and <code>BLOB</code> types. However, these mappings are not enabled
- by default (in particular, by default, <code>std::vector</code> and
- <code>std::array</code> will be treated as containers). To enable the
- alternative mappings for these types we need to specify the database
- type explicitly using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), for
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("BLOB")
- std::vector&lt;char> buf_;
-
- #pragma db type("BINARY(16)")
- unsigned char uuid_[16];
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;char> buffer;
-#pragma db value(buffer) type("BLOB")
-
-#pragma db object
-class object
-{
- ...
-
- buffer buf_; // Mapped to BLOB.
-};
- </pre>
-
- <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
- the Database"</a>) <code>char[N]</code> and
- <code>std::array&lt;char, N></code> parameters are by default passed
- as a string rather than a binary. To pass such parameters as a binary,
- we need to specify the database type explicitly in the
- <code>_val()</code>/<code>_ref()</code> calls. Note also that we
- don't need to do this for the integrated queries, for example:</p>
-
- <pre class="cxx">
-char u[16] = {...};
-
-db.query&lt;object> ("uuid = " + query::_val&lt;odb::mysql::id_blob> (u));
-db.query&lt;object> (query::uuid == query::_ref (u));
- </pre>
-
- <h2><a name="17.2">17.2 MySQL Database Class</a></h2>
-
- <p>The MySQL <code>database</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mysql
- {
- class database: public odb::database
- {
- public:
- database (const char* user,
- const char* passwd,
- const char* db,
- const char* host = 0,
- unsigned int port = 0,
- const char* socket = 0,
- const char* charset = 0,
- unsigned long client_flags = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; user,
- const std::string&amp; passwd,
- const std::string&amp; db,
- const std::string&amp; host = "",
- unsigned int port = 0,
- const std::string* socket = 0,
- const std::string&amp; charset = "",
- unsigned long client_flags = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; user,
- const std::string* passwd,
- const std::string&amp; db,
- const std::string&amp; host = "",
- unsigned int port = 0,
- const std::string* socket = 0,
- const std::string&amp; charset = "",
- unsigned long client_flags = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; user,
- const std::string&amp; passwd,
- const std::string&amp; db,
- const std::string&amp; host,
- unsigned int port,
- const std::string&amp; socket,
- const std::string&amp; charset = "",
- unsigned long client_flags = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; user,
- const std::string* passwd,
- const std::string&amp; db,
- const std::string&amp; host,
- unsigned int port,
- const std::string&amp; socket,
- const std::string&amp; charset = "",
- unsigned long client_flags = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (int&amp; argc,
- char* argv[],
- bool erase = false,
- const std::string&amp; charset = "",
- unsigned long client_flags = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- static void
- print_usage (std::ostream&amp;);
-
- public:
- const char*
- user () const;
-
- const char*
- password () const;
-
- const char*
- db () const;
-
- const char*
- host () const;
-
- unsigned int
- port () const;
-
- const char*
- socket () const;
-
- const char*
- charset () const;
-
- unsigned long
- client_flags () const;
-
- public:
- connection_ptr
- connection ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/mysql/database.hxx></code>
- header file to make this class available in your application.</p>
-
- <p>The overloaded <code>database</code> constructors allow us
- to specify MySQL database parameters that should be used when
- connecting to the database. In MySQL <code>NULL</code> and an
- empty string are treated as the same values for all the
- string parameters except <code>password</code> and
- <code>socket</code>.</p>
-
- <p>The <code>charset</code> argument allows us to specify the client
- character set, that is, the character set in which the application
- will encode its text data. Note that this can be different from
- the MySQL server character set. If this argument is not specified or
- is empty, then the default MySQL client character set is used, normally
- <code>latin1</code>. Commonly used values for this argument are
- <code>latin1</code> (equivalent to Windows cp1252 and similar to
- ISO-8859-1) and <code>utf8</code>. For other possible values
- as well as more information on character set support in MySQL,
- refer to the MySQL documentation.</p>
-
- <p>The <code>client_flags</code> argument allows us to specify various
- MySQL client library flags. For more information on the possible
- values, refer to the MySQL C API documentation. The
- <code>CLIENT_FOUND_ROWS</code> flag is always set by the MySQL ODB
- runtime regardless of whether it was passed in the
- <code>client_flags</code> argument.</p>
-
- <p>The last constructor extracts the database parameters
- from the command line. The following options are recognized:</p>
-
- <pre class="terminal">
- --user &lt;login>
- --password &lt;password>
- --database &lt;name>
- --host &lt;host>
- --port &lt;integer>
- --socket &lt;socket>
- --options-file &lt;file>
- </pre>
-
- <p>The <code>--options-file</code> option allows us to specify some
- or all of the database options in a file with each option appearing
- on a separate line followed by a space and an option value.</p>
-
- <p>If the <code>erase</code> argument to this constructor is true,
- then the above options are removed from the <code>argv</code>
- array and the <code>argc</code> count is updated accordingly.
- This is primarily useful if your application accepts other
- options or arguments and you would like to get the MySQL
- options out of the <code>argv</code> array.</p>
-
- <p>This constructor throws the <code>odb::mysql::cli_exception</code>
- exception if the MySQL option values are missing or invalid.
- See section <a href="#17.4">Section 17.4, "MySQL Exceptions"</a>
- for more information on this exception.</p>
-
- <p>The static <code>print_usage()</code> function prints the list of options
- with short descriptions that are recognized by this constructor.</p>
-
- <p>The last argument to all of the constructors is a pointer to the
- connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
- in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
- non-<code>NULL</code> value, the database instance assumes ownership
- of the factory instance. The connection factory interface as well as
- the available implementations are described in the next section.</p>
-
- <p>The set of accessor functions following the constructors allows us
- to query the parameters of the <code>database</code> instance.</p>
-
- <p>The <code>connection()</code> function returns a pointer to the
- MySQL database connection encapsulated by the
- <code>odb::mysql::connection</code> class. For more information
- on <code>mysql::connection</code>, refer to <a href="#17.3">Section
- 17.3, "MySQL Connection and Connection Factory"</a>.</p>
-
- <h2><a name="17.3">17.3 MySQL Connection and Connection Factory</a></h2>
-
- <p>The <code>mysql::connection</code> class has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mysql
- {
- class connection: public odb::connection
- {
- public:
- connection (database&amp;);
- connection (database&amp;, MYSQL*);
-
- MYSQL*
- handle ();
- };
-
- typedef details::shared_ptr&lt;connection> connection_ptr;
- }
-}
- </pre>
-
- <p>For more information on the <code>odb::connection</code> interface,
- refer to <a href="#3.6">Section 3.6, "Connections"</a>. The first
- overloaded <code>mysql::connection</code> constructor establishes a
- new MySQL connection. The second constructor allows us to create
- a <code>connection</code> instance by providing an already connected
- native MySQL handle. Note that the <code>connection</code>
- instance assumes ownership of this handle. The <code>handle()</code>
- accessor returns the MySQL handle corresponding to the connection.</p>
-
- <p>The <code>mysql::connection_factory</code> abstract class has the
- following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mysql
- {
- class connection_factory
- {
- public:
- virtual void
- database (database&amp;) = 0;
-
- virtual connection_ptr
- connect () = 0;
- };
- }
-}
- </pre>
-
- <p>The <code>database()</code> function is called when a connection
- factory is associated with a database instance. This happens in
- the <code>odb::mysql::database</code> class constructors. The
- <code>connect()</code> function is called whenever a database
- connection is requested.</p>
-
- <p>The two implementations of the <code>connection_factory</code>
- interface provided by the MySQL ODB runtime are
- <code>new_connection_factory</code> and
- <code>connection_pool_factory</code>. You will need to include
- the <code>&lt;odb/mysql/connection-factory.hxx></code>
- header file to make the <code>connection_factory</code> interface
- and these implementation classes available in your application.</p>
-
- <p>The <code>new_connection_factory</code> class creates a new
- connection whenever one is requested. When a connection is no
- longer needed, it is released and closed. The
- <code>new_connection_factory</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mysql
- {
- class new_connection_factory: public connection_factory
- {
- public:
- new_connection_factory ();
- };
-};
- </pre>
-
- <p>The <code>connection_pool_factory</code> class implements a
- connection pool. It has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mysql
- {
- class connection_pool_factory: public connection_factory
- {
- public:
- connection_pool_factory (std::size_t max_connections = 0,
- std::size_t min_connections = 0,
- bool ping = true);
-
- protected:
- class pooled_connection: public connection
- {
- public:
- pooled_connection (database_type&amp;);
- pooled_connection (database_type&amp;, MYSQL*);
- };
-
- typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
-
- virtual pooled_connection_ptr
- create ();
- };
-};
- </pre>
-
- <p>The <code>max_connections</code> argument in the
- <code>connection_pool_factory</code> constructor specifies the maximum
- number of concurrent connections that this pool factory will
- maintain. Similarly, the <code>min_connections</code> argument
- specifies the minimum number of available connections that
- should be kept open. The <code>ping</code> argument specifies
- whether the factory should validate the connection before
- returning it to the caller.</p>
-
- <p>Whenever a connection is requested, the pool factory first
- checks if there is an unused connection that can be returned.
- If there is none, the pool factory checks the
- <code>max_connections</code> value to see if a new connection
- can be created. If the total number of connections maintained
- by the pool is less than this value, then a new connection is
- created and returned. Otherwise, the caller is blocked until
- a connection becomes available.</p>
-
- <p>When a connection is released, the pool factory first checks
- if there are blocked callers waiting for a connection. If so, then
- one of them is unblocked and is given the connection. Otherwise,
- the pool factory checks whether the total number of connections
- maintained by the pool is greater than the <code>min_connections</code>
- value. If that's the case, the connection is closed. Otherwise, the
- connection is added to the pool of available connections to be
- returned on the next request. In other words, if the number of
- connections maintained by the pool exceeds <code>min_connections</code>
- and there are no callers waiting for a new connection,
- then the pool will close the excess connections.</p>
-
- <p>If the <code>max_connections</code> value is 0, then the pool will
- create a new connection whenever all of the existing connections
- are in use. If the <code>min_connections</code> value is 0, then
- the pool will never close a connection and instead maintain all
- the connections that were ever created.</p>
-
- <p>Connection validation (the <code>ping</code> argument) is useful
- if your application may experience long periods of inactivity. In
- such cases the MySQL server may close network connections that have
- been inactive for too long. If during connection validation the pool
- factory detects that the connection has been terminated, it silently
- closes it and tries to find or create another connection instead.</p>
-
- <p>The <code>create()</code> virtual function is called whenever the
- pool needs to create a new connection. By deriving from the
- <code>connection_pool_factory</code> class and overriding this
- function we can implement custom connection establishment
- and configuration.</p>
-
- <p>If you pass <code>NULL</code> as the connection factory to
- one of the <code>database</code> constructors, then the
- <code>connection_pool_factory</code> instance will be
- created by default with the min and max connections values
- set to <code>0</code> and connection validation enabled.
- The following code fragment shows how we can pass our own
- connection factory instance:</p>
-
- <pre class="cxx">
-#include &lt;odb/database.hxx>
-
-#include &lt;odb/mysql/database.hxx>
-#include &lt;odb/mysql/connection-factory.hxx>
-
-int
-main (int argc, char* argv[])
-{
- auto_ptr&lt;odb::mysql::connection_factory> f (
- new odb::mysql::connection_pool_factory (20));
-
- auto_ptr&lt;odb::database> db (
- new mysql::database (argc, argv, false, 0, f));
-}
- </pre>
-
- <h2><a name="17.4">17.4 MySQL Exceptions</a></h2>
-
- <p>The MySQL ODB runtime library defines the following MySQL-specific
- exceptions:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mysql
- {
- class database_exception: odb::database_exception
- {
- public:
- unsigned int
- error () const;
-
- const std::string&amp;
- sqlstate () const;
-
- const std::string&amp;
- message () const;
-
- virtual const char*
- what () const throw ();
- };
-
- class cli_exception: odb::exception
- {
- public:
- virtual const char*
- what () const throw ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/mysql/exceptions.hxx></code>
- header file to make these exceptions available in your application.</p>
-
- <p>The <code>odb::mysql::database_exception</code> is thrown if
- a MySQL database operation fails. The MySQL-specific error
- information is accessible via the <code>error()</code>,
- <code>sqlstate()</code>, and <code>message()</code> functions.
- All this information is also combined and returned in a
- human-readable form by the <code>what()</code> function.</p>
-
- <p>The <code>odb::mysql::cli_exception</code> is thrown by the
- command line parsing constructor of the <code>odb::mysql::database</code>
- class if the MySQL option values are missing or invalid. The
- <code>what()</code> function returns a human-readable description
- of an error.</p>
-
- <h2><a name="17.5">17.5 MySQL Limitations</a></h2>
-
- <p>The following sections describe MySQL-specific limitations imposed
- by the current MySQL and ODB runtime versions.</p>
-
- <h3><a name="17.5.1">17.5.1 Foreign Key Constraints</a></h3>
-
- <p>ODB relies on standard SQL behavior which requires that foreign
- key constraints checking is deferred until the transaction is
- committed. The only behaviors supported by MySQL are to either
- check such constraints immediately (InnoDB engine) or to ignore
- foreign key constraints altogether (all other engines). As a
- result, by default, schemas generated by the ODB compiler for
- MySQL have foreign key definitions commented out. They are
- retained only for documentation.</p>
-
- <p>You can override the default behavior and instruct the ODB
- compiler to generate non-deferrable foreign keys by specifying
- the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
- compiler option. Note, however, that in this case the order in
- which you persist, update, and erase objects within a transaction
- becomes important.</p>
-
- <h2><a name="17.6">17.6 MySQL Index Definitions</a></h2>
-
- <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
- "Index Definition Pragmas"</a>) is used to define a MySQL index,
- the <code>type</code> clause specifies the index type (for example,
- <code>UNIQUE</code>, <code>FULLTEXT</code>, <code>SPATIAL</code>),
- the <code>method</code> clause specifies the index method (for
- example, <code>BTREE</code>, <code>HASH</code>), and the
- <code>options</code> clause is not used. The column options
- can be used to specify column length limits and the sort order.
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- std::string name_;
-
- #pragma db index method("HASH") member(name_, "(100) DESC")
-};
- </pre>
-
- <h2><a name="17.7">17.7 MySQL Stored Procedures</a></h2>
-
- <p>ODB native views (<a href="#10.6">Section 10.6, "Native Views"</a>)
- can be used to call MySQL stored procedures. For example, assuming
- we are using the <code>person</code> class from <a href="#2">Chapter
- 2, "Hello World Example"</a> (and the corresponding <code>person</code>
- table), we can create a stored procedure that given the min and max
- ages returns some information about all the people in that range:</p>
-
- <pre class="sql">
-CREATE PROCEDURE person_range (
- IN min_age SMALLINT,
- IN max_age SMALLINT)
-BEGIN
- SELECT age, first, last FROM person
- WHERE age >= min_age AND age &lt;= max_age;
-END
- </pre>
-
- <p>Given the above stored procedure we can then define an ODB view
- that can be used to call it and retrieve its result:</p>
-
- <pre class="cxx">
-#pragma db view query("CALL person_range((?))")
-struct person_range
-{
- unsigned short age;
- std::string first;
- std::string last;
-};
- </pre>
-
- <p>The following example shows how we can use the above view to
- print the list of people in a specific age range:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;person_range> query;
-typedef odb::result&lt;person_range> result;
-
-transaction t (db.begin ());
-
-result r (
- db.query&lt;person_range> (
- query::_val (1) + "," + query::_val (18)));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
- cerr &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->age &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>Note that as with all native views, the order and types of data members
- must match those of columns in the <code>SELECT</code> list inside
- the stored procedure.</p>
-
- <p>There are also a number of limitations when it comes to support for
- MySQL stored procedures in ODB views. First of all, you have to use
- MySQL server and client libraries version 5.5.3 or later since this
- is the version in which support for calling stored procedures with
- prepared statements was first added (the
- <code>mysql_stmt_next_result()</code> function).</p>
-
- <p>In MySQL, a stored procedure can produce multiple results.
- For example, if a stored procedure executes several
- <code>SELECT</code> statements, then the result of calling such
- a procedure consists of two row sets, one for each <code>SELECT</code>
- statement. Additionally, if the procedure has any <code>OUT</code>
- or <code>INOUT</code> parameters, then their values are returned as
- an additional special row set containing only a single row.
- Because such multiple row sets can contain varying number
- and type of columns, they cannot be all extracted into a
- single view. As a result, an ODB view will only extract the
- data from the first row set and ignore all the subsequent
- ones.</p>
-
- <p>In particular, this means that we can use an ODB view to extract
- the values of the <code>OUT</code> and <code>INOUT</code>
- parameters provided that the stored procedure does not generate
- any other row sets. For example:</p>
-
- <pre class="sql">
-CREATE PROCEDURE person_min_max_age (
- OUT min_age SMALLINT,
- OUT max_age SMALLINT)
-BEGIN
- SELECT MIN(age), MAX(age) INTO min_age, max_age FROM person;
-END
- </pre>
-
- <pre class="cxx">
-#pragma db view query("CALL person_min_max_age((?))")
-struct person_min_max_age
-{
- unsigned short min_age;
- unsigned short max_age;
-};
- </pre>
-
- <pre class="cxx">
-typedef odb::query&lt;person_min_max_age> query;
-
-transaction t (db.begin ());
-
-// We know this query always returns a single row, so use query_value().
-// We have to pass dummy values for OUT parameters.
-//
-person_min_max_age mma (
- db.query_value&lt;person_min_max_age> (
- query::_val (0) + "," + query::_val (0)));
-
-cerr &lt;&lt; mma.min_age &lt;&lt; " " &lt;&lt; mma.max_age &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>Another limitation that stems from having multiple results is the
- inability to cache the result of a stored procedure call. In
- other words, a MySQL stored procedure call always produces an
- uncached query result (<a href="#4.4">Section 4.4, "Query
- Result"</a>).</p>
-
- <hr class="page-break"/>
- <h1><a name="18">18 SQLite Database</a></h1>
-
- <p>To generate support code for the SQLite database you will need
- to pass the "<code>--database&nbsp;sqlite</code>"
- (or "<code>-d&nbsp;sqlite</code>") option to the ODB compiler.
- Your application will also need to link to the SQLite ODB runtime
- library (<code>libodb-sqlite</code>). All SQLite-specific ODB
- classes are defined in the <code>odb::sqlite</code> namespace.</p>
-
- <h2><a name="18.1">18.1 SQLite Type Mapping</a></h2>
-
- <p>The following table summarizes the default mapping between basic
- C++ value types and SQLite database types. This mapping can be
- customized on the per-type and per-member basis using the ODB
- Pragma Language (<a href="#14">Chapter 14, "ODB Pragma
- Language"</a>).</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>C++ Type</th>
- <th>SQLite Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>bool</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char</code></td>
- <td><code>TEXT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>signed char</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned char</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>short</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned short</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>int</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned int</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long long</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long long</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>float</code></td>
- <td><code>REAL</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>double</code></td>
- <td><code>REAL</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>std::string</code></td>
- <td><code>TEXT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char[N]</code></td>
- <td><code>TEXT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>std::wstring (Windows only)</code></td>
- <td><code>TEXT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>wchar_t[N] (Windows only)</code></td>
- <td><code>TEXT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>odb::sqlite::text</code></td>
- <td><code>TEXT (STREAM)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>odb::sqlite::blob</code></td>
- <td><code>BLOB (STREAM)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
- </table>
-
- <p>It is possible to map the <code>char</code> C++ type to the
- <code>INTEGER</code> SQLite type using the <code>db&nbsp;type</code>
- pragma (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>).</p>
-
- <p>SQLite represents the <code>NaN</code> <code>FLOAT</code> value
- as a <code>NULL</code> value. As a result, columns of the
- <code>float</code> and <code>double</code> types are by default
- declared as <code>NULL</code>. However, you can override this by
- explicitly declaring them as <code>NOT NULL</code> with the
- <code>db&nbsp;not_null</code> pragma (<a href="#14.4.6">Section
- 14.4.6, "<code>null/not_null</code>"</a>).</p>
-
- <p>Additionally, by default, C++ enums and C++11 enum classes are
- automatically mapped to the SQLite <code>INTEGER</code> type with
- the default <code>NULL</code> semantics being <code>NOT NULL</code>.
- For example:</p>
-
- <pre class="cxx">
-enum color {red, green, blue};
-enum class taste: unsigned char
-{
- bitter = 1,
- sweet,
- sour = 4,
- salty
-};
-
-#pragma db object
-class object
-{
- ...
-
- color color_; // Automatically mapped to INTEGER.
- taste taste_; // Automatically mapped to INTEGER.
-};
- </pre>
-
- <p>Note also that SQLite only operates with signed integers and the largest
- value that an SQLite database can store is a signed 64-bit integer. As
- a result, greater <code>unsigned&nbsp;long</code> and
- <code>unsigned&nbsp;long&nbsp;long</code> values will be represented in
- the database as negative values.</p>
-
- <p>It is also possible to add support for additional SQLite types,
- such as <code>NUMERIC</code>. For more information, refer to
- <a href="#14.8">Section 14.8, "Database Type Mapping
- Pragmas"</a>.</p>
-
- <h3><a name="18.1.1">18.1.1 String Type Mapping</a></h3>
-
- <p>The SQLite ODB runtime library provides support for mapping the
- <code>std::array&lt;char, N></code> and, on Windows,
- <code>std::array&lt;wchar_t, N></code> types to the SQLite
- <code>TEXT</code> type. However, this mapping is not enabled by
- default (in particular, by default, <code>std::array</code> will
- be treated as a container). To enable the alternative mapping for
- this type we need to specify the database type explicitly using
- the <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section
- 14.4.3, "<code>type</code>"</a>), for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("TEXT")
- std::array&lt;char, 128> name_;
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-typedef std::array&lt;char, 128> name_type;
-#pragma db value(name_type) type("TEXT")
-
-#pragma db object
-class object
-{
- ...
-
- name_type name_; // Mapped to TEXT.
-};
- </pre>
-
- <p>The <code>char[N]</code>, <code>std::array&lt;char, N></code>,
- <code>wchar_t[N]</code>, and <code>std::array&lt;wchar_t, N></code>
- values may or may not be zero-terminated. When extracting such values
- from the database, ODB will append the zero terminator if there is
- enough space.</p>
-
- <h3><a name="18.1.2">18.1.2 Binary Type Mapping</a></h3>
-
- <p>The SQLite ODB runtime library provides support for mapping the
- <code>std::vector&lt;char></code>,
- <code>std::vector&lt;unsigned&nbsp;char></code>,
- <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
- <code>std::array&lt;char, N></code>, and
- <code>std::array&lt;unsigned char, N></code>
- types to the SQLite <code>BLOB</code> type. However, these mappings
- are not enabled by default (in particular, by default,
- <code>std::vector</code> and <code>std::array</code> will be treated
- as containers). To enable the alternative mappings for these types
- we need to specify the database type explicitly using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("BLOB")
- std::vector&lt;char> buf_;
-
- #pragma db type("BLOB")
- unsigned char uuid_[16];
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;char> buffer;
-#pragma db value(buffer) type("BLOB")
-
-#pragma db object
-class object
-{
- ...
-
- buffer buf_; // Mapped to BLOB.
-};
- </pre>
-
- <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
- the Database"</a>) <code>char[N]</code> and
- <code>std::array&lt;char, N></code> parameters are by default passed
- as a string rather than a binary. To pass such parameters as a binary,
- we need to specify the database type explicitly in the
- <code>_val()</code>/<code>_ref()</code> calls. Note also that we
- don't need to do this for the integrated queries, for example:</p>
-
- <pre class="cxx">
-char u[16] = {...};
-
-db.query&lt;object> ("uuid = " + query::_val&lt;odb::sqlite::id_blob> (u));
-db.query&lt;object> (query::uuid == query::_ref (u));
- </pre>
-
- <h3><a name="18.1.3">18.1.3 Incremental <code>BLOB</code>/<code>TEXT</code> I/O</a></h3>
-
- <p>This section describes the SQLite ODB runtime library support for
- incremental reading and writing of <code>BLOB</code> and
- <code>TEXT</code> values. The provided API is a thin wrapper
- around the native SQLite <code>sqlite3_blob_*()</code> function
- family. As a result, it is highly recommended that you familiarize
- yourself with the semantics of this SQLite functionality before
- continuing with this section.</p>
-
- <p>The SQLite runtime provides the <code>blob</code> and
- <code>text</code> types that can be used to represent
- <code>BLOB</code> and <code>TEXT</code> data members
- that will be read/written using the incremental I/O.
- For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/sqlite/blob.hxx>
-#include &lt;odb/sqlite/text.hxx>
-
-#pragma db object
-class object
-{
-public
- #pragma db id auto
- unsigned long id;
-
- odb::sqlite::blob b; // Mapped to BLOB.
- odb::sqlite::text t; // Mapped to TEXT.
-};
- </pre>
-
- <p>The <code>blob</code> and <code>text</code> types should be
- viewed as <em>descriptors</em> of the <code>BLOB</code> and
- <code>TEXT</code> values (rather than the values themselves)
- that can be used to <em>open</em> the values for reading or
- writing. These two types have an identical interface that
- is presented below. Notice that it is essentially the list
- of arguments (except for <code>size</code> which is discussed
- below) to the <code>sqlite3_blob_open()</code> function:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace sqlite
- {
- class blob|text
- {
- public:
- explicit
- blob|text (std::size_t = 0);
-
- std::size_t size ()
- void size (std::size_t);
-
- const std::string&amp; db () const;
- const std::string&amp; table () const;
- const std::string&amp; column () const;
- long long rowid () const;
-
- void
- clear ();
- };
- }
-}
- </pre>
-
- <p>To read/write data from/to a incremental <code>BLOB</code> or
- <code>TEXT</code> value we use the corresponding
- <code>blob_stream</code> and <code>text_stream</code>
- stream types. Their interfaces closely mimic the
- underlying <code>sqlite3_blob_*()</code> functions
- and are presented below. Note that in order to create
- a stream we have to pass the corresponding descriptor:</p>
-
- <pre class="cxx">
-#include &lt;odb/sqlite/stream.hxx>
-
-namespace odb
-{
- namespace sqlite
- {
- class stream
- {
- public:
- stream (const char* db,
- const char* table,
- const char* column,
- long long rowid,
- bool rw);
-
- std::size_t
- size () const;
-
- // The following two functions throw std::invalid_argument if
- // offset + n is past size().
- //
- void
- read (void* buf, std::size_t n, std::size_t offset = 0);
-
- void
- write (const void* buf, std::size_t n, std::size_t offset = 0);
-
- sqlite3_blob*
- handle () const;
-
- // Close without reporting errors, if any.
- //
- ~stream ();
-
- // Close with reporting errors, if any.
- //
- void
- close ();
-
- // Open the same BLOB but in a different row. Can be faster
- // than creating a new stream instance. Note that the stream
- // must be in the open state prior to calling this function.
- //
- void
- reopen (long long rowid);
- };
- }
-}
-
-#include &lt;odb/sqlite/blob-stream.hxx>
-
-namespace odb
-{
- namespace sqlite
- {
- class blob_stream: public stream
- {
- public:
- blob_stream (const blob&amp;, bool rw);
- };
- }
-}
-
-#include &lt;odb/sqlite/text-stream.hxx>
-
-namespace odb
-{
- namespace sqlite
- {
- class text_stream: public stream
- {
- public:
- text_stream (const text&amp;, bool rw);
- };
- }
-}
- </pre>
-
- <p>The <code>rw</code> argument to the constructors above
- specifies whether to open the value for reading only
- (<code>false</code>) or to read and write
- (<code>true</code>).</p>
-
- <p>In SQLite the incremental <code>BLOB</code> and
- <code>TEXT</code> sizes are fixed in the sense that
- they must be specified before the object is persisted
- or updated and the following write operations can
- only write that much data. This is what the <code>size</code>
- data member in the descriptors is for. You can also determine
- the size of the opened value, for both reading and writing,
- using the <code>size()</code> stream function. The
- following example puts all of this together:</p>
-
- <pre class="cxx">
-#include &lt;odb/sqlite/blob-stream.hxx>
-#include &lt;odb/sqlite/text-stream.hxx>
-
-string txt (1024 * 1024, 't');
-vector&lt;char> blb (1024 * 1024, 'b');
-
-object o;
-
-// Persist.
-//
-{
- transaction tx (db.begin ());
-
- // Specify the sizes of the values before calling persist().
- //
- o.t.size (txt.size ());
- o.b.size (blb.size ());
-
- db.persist (o);
-
- // Write the data.
- //
- blob_stream bs (o.b, true); // Open for read/write.
- assert (bs.size () == blb.size ());
- bs.write (blb.data (), blb.size ());
-
- text_stream ts (o.t, true); // Open for read/write.
- assert (ts.size () == txt.size ());
- ts.write (txt.data (), txt.size ());
-
- tx.commit ();
-}
-
-// Load.
-//
-{
- transaction tx (db.begin ());
- auto_ptr&lt;object> p (db.load&lt;object> (o.id));
-
- text_stream ts (p->t, false); // Open for reading.
- vector&lt;char> t (ts.size () + 1, '\0');
- ts.read (t.data (), t.size () - 1);
- assert (string (t.data ()) == txt);
-
- blob_stream bs (p->b, false); // Open for reading.
- vector&lt;char> b (bs.size (), '\0');
- bs.read (b.data (), b.size ());
- assert (b == blb);
-
- tx.commit ();
-}
-
-// Update
-//
-txt.resize (txt.size () + 1, 't');
-txt[0] = 'A';
-txt[txt.size () - 1] = 'Z';
-
-blb.resize (blb.size () - 1);
-blb.front () = 'A';
-blb.back () = 'Z';
-
-{
- transaction tx (db.begin ());
-
- // Specify the new sizes of the values before calling update().
- //
- o.t.size (txt.size ());
- o.b.size (blb.size ());
-
- db.update (o);
-
- // Write the data.
- //
- blob_stream bs (o.b, true);
- bs.write (blb.data (), blb.size ());
-
- text_stream ts (o.t, true);
- ts.write (txt.data (), txt.size ());
-
- tx.commit ();
-}
- </pre>
-
- <p>For the most part, the incremental <code>BLOB</code> and
- <code>TEXT</code> descriptors can be used as any other
- simple values. Specifically, they can be used as container
- elements (<a href="#5">Chapter 5, "Containers"</a>), as
- <code>NULL</code>-able values (<a href="#7.3">Section 7.3,
- "Pointers and NULL Value Semantics"</a>), and in views
- (<a href="#10">Chapter 10, "Views"</a>). The following
- example illustrates the use within a view:</p>
-
- <pre class="cxx">
-#pragma db view object(object)
-struct load_b
-{
- odb::sqlite::blob b;
-};
-
-typedef odb::query&lt;load_b> query;
-
-transaction tx (db.begin ());
-
-for (load_b&amp; lb: db.query&lt;load_b> (query::t == "test"))
-{
- blob_stream bs (lb.b, false);
- vector&lt;char> b (bs.size (), '\0');
- bs.read (b.data (), b.size ());
-}
-
-tx.commit ();
- </pre>
-
- <p>However, being a low-level, SQLite-specific mechanism, the
- incremental I/O has a number of nuances that should be kept in
- mind. Firstly, the streams should be opened within a transaction
- and, unless already closed, they will be automatically closed
- when the transaction is committed or rolled back. The following
- modification of the previous example helps to illustrate this
- point:</p>
-
- <pre class="cxx">
-{
- transaction tx (db.begin ());
-
- // ...
-
- db.persist (o);
-
- blob_stream bs (o.b, true);
-
- tx.commit ();
-
- // ERROR: stream is closed.
- //
- bs.write (blb.data (), blb.size ());
-}
-
-// ERROR: not in transaction.
-//
-text_stream ts (o.t, true);
- </pre>
-
- <p>Because loading an object with an incremental <code>BLOB</code> or
- <code>TEXT</code> value involves additional actions after the
- database function returns (that is, reading the actual data),
- certain commonly-expected "round-trip" assumptions will no
- longer hold unless special steps are taken, for instance
- (again, continuing with our example):</p>
-
- <pre class="cxx">
-transaction tx (db.begin ());
-
-auto_ptr&lt;object> p (db.load&lt;object> (o.id));
-p->name = "foo"; // Update some other member.
-db.update (*p); // Bad behavior: incremental BLOB/TEXT invalidated.
-
-tx.commit ();
- </pre>
-
- <p>One way to restore the expected behavior is to place the
- incremental <code>BLOB</code> and <code>TEXT</code> values
- into their own, separately loaded/updated sections
- (<a href="#9">Chapter 9, "Sections"</a>). The alternative
- approach would be to perform the incremental I/O as part
- of the database operation <code>post_*</code> callbacks
- (<a href="#14.1.7">Section 14.1.7, "<code>callback</code>"</a>).</p>
-
- <p>Finally, note that when using incremental <code>TEXT</code>
- values, the data that we read/write is the raw bytes in
- the encoding used by the database (<code>UTF-8</code> by
- default; see SQLite <code>PRAGMA encoding</code> documentation
- for details).</p>
-
- <h2><a name="18.2">18.2 SQLite Database Class</a></h2>
-
- <p>The SQLite <code>database</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace sqlite
- {
- class database: public odb::database
- {
- public:
- database (const std::string&amp; name,
- int flags = SQLITE_OPEN_READWRITE,
- bool foreign_keys = true,
- const std::string&amp; vfs = "",
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
-#ifdef _WIN32
- database (const std::wstring&amp; name,
- int flags = SQLITE_OPEN_READWRITE,
- bool foreign_keys = true,
- const std::string&amp; vfs = "",
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-#endif
-
- database (int&amp; argc,
- char* argv[],
- bool erase = false,
- int flags = SQLITE_OPEN_READWRITE,
- bool foreign_keys = true,
- const std::string&amp; vfs = "",
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- static void
- print_usage (std::ostream&amp;);
-
- public:
- const std::string&amp;
- name () const;
-
- int
- flags () const;
-
- public:
- transaction
- begin_immediate ();
-
- transaction
- begin_exclusive ();
-
- public:
- connection_ptr
- connection ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/sqlite/database.hxx></code>
- header file to make this class available in your application.</p>
-
- <p>The first constructor opens the specified SQLite database. The
- <code>name</code> argument is the database file name to open in
- the UTF-8 encoding. If this argument is empty, then a temporary,
- on-disk database is created. If this argument is the
- <code>:memory:</code> special value, then a temporary, in-memory
- database is created. The <code>flags</code> argument allows us to
- specify SQLite opening flags. For more information on the possible
- values, refer to the <code>sqlite3_open_v2()</code> function description
- in the SQLite C API documentation. The <code>foreign_keys</code>
- argument specifies whether foreign key constraints checking
- should be enabled. See <a href="#18.5.3">Section 18.5.3,
- "Foreign Key Constraints"</a> for more information on foreign
- keys. The <code>vfs</code> argument specifies the SQLite
- virtual file system module that should be used to access the
- database. If this argument is empty, then the default vfs module
- is used. Again, refer to the <code>sqlite3_open_v2()</code> function
- documentation for detail.</p>
-
- <p>The following example shows how we can open the <code>test.db</code>
- database in the read-write mode and create it if it does not exist:</p>
-
- <pre class="cxx">
-auto_ptr&lt;odb::database> db (
- new odb::sqlite::database (
- "test.db",
- SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE));
- </pre>
-
- <p>The second constructor is the same as the first except that the database
- name is passes as <code>std::wstring</code> in the UTF-16 encoding. This
- constructor is only available when compiling for Windows.</p>
-
- <p>The third constructor extracts the database parameters from the
- command line. The following options are recognized:</p>
-
- <pre class="terminal">
- --database &lt;name>
- --create
- --read-only
- --options-file &lt;file>
- </pre>
-
- <p>By default, this constructor opens the database in the read-write mode
- (<code>SQLITE_OPEN_READWRITE</code> flag). If the <code>--create</code>
- flag is specified, then the database file is created if it does
- not already exist (<code>SQLITE_OPEN_CREATE</code> flag). If the
- <code>--read-only</code> flag is specified, then the database is
- opened in the read-only mode (<code>SQLITE_OPEN_READONLY</code>
- flag instead of <code>SQLITE_OPEN_READWRITE</code>). The
- <code>--options-file</code> option allows us to specify some
- or all of the database options in a file with each option appearing
- on a separate line followed by a space and an option value.</p>
-
- <p>If the <code>erase</code> argument to this constructor is true,
- then the above options are removed from the <code>argv</code>
- array and the <code>argc</code> count is updated accordingly.
- This is primarily useful if your application accepts other
- options or arguments and you would like to get the SQLite
- options out of the <code>argv</code> array.</p>
-
- <p>The <code>flags</code> argument has the same semantics as in
- the first constructor. Flags from the command line always override
- the corresponding values specified with this argument.</p>
-
- <p>The third constructor throws the <code>odb::sqlite::cli_exception</code>
- exception if the SQLite option values are missing or invalid.
- See <a href="#18.4">Section 18.4, "SQLite Exceptions"</a>
- for more information on this exception.</p>
-
- <p>The static <code>print_usage()</code> function prints the list of options
- with short descriptions that are recognized by the third constructor.</p>
-
- <p>The last argument to all of the constructors is a pointer to the
- connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
- in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
- non-<code>NULL</code> value, the database instance assumes ownership
- of the factory instance. The connection factory interface as well as
- the available implementations are described in the next section.</p>
-
- <p>The set of accessor functions following the constructors allows us
- to query the parameters of the <code>database</code> instance.</p>
-
- <p>The <code>begin_immediate()</code> and <code>begin_exclusive()</code>
- functions are the SQLite-specific extensions to the standard
- <code>odb::database::begin()</code> function (see
- <a href="#3.5">Section 3.5, "Transactions"</a>). They allow us
- to start an immediate (<code>BEGIN IMMEDIATE</code>) and an exclusive
- (<code>BEGIN EXCLUSIVE</code>) SQLite transaction, respectively.
- For more information on the semantics of the immediate and exclusive
- transactions, refer to the <code>BEGIN</code> statement description
- in the SQLite documentation.</p>
-
- <p>The <code>connection()</code> function returns a pointer to the
- SQLite database connection encapsulated by the
- <code>odb::sqlite::connection</code> class. For more information
- on <code>sqlite::connection</code>, refer to <a href="#18.3">Section
- 18.3, "SQLite Connection and Connection Factory"</a>.</p>
-
- <h2><a name="18.3">18.3 SQLite Connection and Connection Factory</a></h2>
-
- <p>The <code>sqlite::connection</code> class has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace sqlite
- {
- class connection: public odb::connection
- {
- public:
- connection (database&amp;, int extra_flags = 0);
- connection (database&amp;, sqlite3*);
-
- transaction
- begin_immediate ();
-
- transaction
- begin_exclusive ();
-
- sqlite3*
- handle ();
- };
-
- typedef details::shared_ptr&lt;connection> connection_ptr;
- }
-}
- </pre>
-
- <p>For more information on the <code>odb::connection</code> interface,
- refer to <a href="#3.6">Section 3.6, "Connections"</a>. The first
- overloaded <code>sqlite::connection</code> constructor opens
- a new SQLite connection. The <code>extra_flags</code> argument can
- be used to specify extra <code>sqlite3_open_v2()</code> flags
- that are combined with the flags specified in the
- <code>sqlite::database</code> constructor. The second constructor
- allows us to create a <code>connection</code> instance by providing
- an already open native SQLite handle. Note that the
- <code>connection</code> instance assumes ownership of this handle.</p>
-
- <p>The <code>begin_immediate()</code> and <code>begin_exclusive()</code>
- functions allow us to start an immediate and an exclusive SQLite
- transaction on the connection, respectively. Their semantics are
- equivalent to the corresponding functions defined in the
- <code>sqlite::database</code> class (<a href="#18.2">Section 18.2,
- "SQLite Database Class"</a>). The <code>handle()</code> accessor
- returns the SQLite handle corresponding to the connection.</p>
-
- <p>The <code>sqlite::connection_factory</code> abstract class has the
- following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace sqlite
- {
- class connection_factory
- {
- public:
- virtual void
- database (database&amp;) = 0;
-
- virtual connection_ptr
- connect () = 0;
- };
- }
-}
- </pre>
-
- <p>The <code>database()</code> function is called when a connection
- factory is associated with a database instance. This happens in
- the <code>odb::sqlite::database</code> class constructors. The
- <code>connect()</code> function is called whenever a database
- connection is requested.</p>
-
- <p>The three implementations of the <code>connection_factory</code>
- interface provided by the SQLite ODB runtime library are
- <code>single_connection_factory</code>,
- <code>new_connection_factory</code>, and
- <code>connection_pool_factory</code>. You will need to include
- the <code>&lt;odb/sqlite/connection-factory.hxx></code>
- header file to make the <code>connection_factory</code> interface
- and these implementation classes available in your application.</p>
-
- <p>The <code>single_connection_factory</code> class creates a
- single connection that is shared between all the threads in
- an application. If the connection is currently not in use,
- then it is returned to the caller. Otherwise, the caller is
- blocked until the connection becomes available. The
- <code>single_connection_factory</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace sqlite
- {
- class single_connection_factory: public connection_factory
- {
- public:
- single_connection_factory ();
-
- protected:
- class single_connection: public connection
- {
- public:
- single_connection (database&amp;, int extra_flags = 0);
- single_connection (database&amp;, sqlite3*);
- };
-
- typedef details::shared_ptr&lt;single_connection> single_connection_ptr;
-
- virtual single_connection_ptr
- create ();
- };
-};
- </pre>
-
- <p>The <code>create()</code> virtual function is called when the
- factory needs to create the connection. By deriving from the
- <code>single_connection_factory</code> class and overriding this
- function we can implement custom connection establishment
- and configuration.</p>
-
- <p>The <code>new_connection_factory</code> class creates a new
- connection whenever one is requested. When a connection is no
- longer needed, it is released and closed. The
- <code>new_connection_factory</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace sqlite
- {
- class new_connection_factory: public connection_factory
- {
- public:
- new_connection_factory ();
- };
-};
- </pre>
-
- <p>The <code>connection_pool_factory</code> class implements a
- connection pool. It has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace sqlite
- {
- class connection_pool_factory: public connection_factory
- {
- public:
- connection_pool_factory (std::size_t max_connections = 0,
- std::size_t min_connections = 0);
-
- protected:
- class pooled_connection: public connection
- {
- public:
- pooled_connection (database_type&amp;, int extra_flags = 0);
- pooled_connection (database_type&amp;, sqlite3*);
- };
-
- typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
-
- virtual pooled_connection_ptr
- create ();
- };
-};
- </pre>
-
- <p>The <code>max_connections</code> argument in the
- <code>connection_pool_factory</code> constructor specifies the maximum
- number of concurrent connections that this pool factory will
- maintain. Similarly, the <code>min_connections</code> argument
- specifies the minimum number of available connections that
- should be kept open.</p>
-
- <p>Whenever a connection is requested, the pool factory first
- checks if there is an unused connection that can be returned.
- If there is none, the pool factory checks the
- <code>max_connections</code> value to see if a new connection
- can be created. If the total number of connections maintained
- by the pool is less than this value, then a new connection is
- created and returned. Otherwise, the caller is blocked until
- a connection becomes available.</p>
-
- <p>When a connection is released, the pool factory first checks
- if there are blocked callers waiting for a connection. If so, then
- one of them is unblocked and is given the connection. Otherwise,
- the pool factory checks whether the total number of connections
- maintained by the pool is greater than the <code>min_connections</code>
- value. If that's the case, the connection is closed. Otherwise, the
- connection is added to the pool of available connections to be
- returned on the next request. In other words, if the number of
- connections maintained by the pool exceeds <code>min_connections</code>
- and there are no callers waiting for a new connection,
- then the pool will close the excess connections.</p>
-
- <p>If the <code>max_connections</code> value is 0, then the pool will
- create a new connection whenever all of the existing connections
- are in use. If the <code>min_connections</code> value is 0, then
- the pool will never close a connection and instead maintain all
- the connections that were ever created.</p>
-
- <p>The <code>create()</code> virtual function is called whenever the
- pool needs to create a new connection. By deriving from the
- <code>connection_pool_factory</code> class and overriding this
- function we can implement custom connection establishment
- and configuration.</p>
-
- <p>By default, connections created by <code>new_connection_factory</code>
- and <code>connection_pool_factory</code> enable the SQLite shared cache
- mode and use the unlock notify functionality to aid concurrency. To
- disable the shared cache mode you can pass the
- <code>SQLITE_OPEN_PRIVATECACHE</code> flag when creating the database
- instance. For more information on the shared cache mode refer to the
- SQLite documentation.</p>
-
- <p>If you pass <code>NULL</code> as the connection factory to one of the
- <code>database</code> constructors, then the <code>connection_pool_factory</code>
- instance will be created by default with the min and max connections
- values set to <code>0</code>. The following code fragment shows how we
- can pass our own connection factory instance:</p>
-
- <pre class="cxx">
-#include &lt;odb/database.hxx>
-
-#include &lt;odb/sqlite/database.hxx>
-#include &lt;odb/sqlite/connection-factory.hxx>
-
-int
-main (int argc, char* argv[])
-{
- auto_ptr&lt;odb::sqlite::connection_factory> f (
- new odb::sqlite::connection_pool_factory (20));
-
- auto_ptr&lt;odb::database> db (
- new sqlite::database (argc, argv, false, SQLITE_OPEN_READWRITE, f));
-}
- </pre>
-
- <h2><a name="18.4">18.4 SQLite Exceptions</a></h2>
-
- <p>The SQLite ODB runtime library defines the following SQLite-specific
- exceptions:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace sqlite
- {
- class forced_rollback: odb::recoverable
- {
- public:
- virtual const char*
- what () const throw ();
- };
-
- class database_exception: odb::database_exception
- {
- public:
- int
- error () const
-
- int
- extended_error () const;
-
- const std::string&amp;
- message () const;
-
- virtual const char*
- what () const throw ();
- };
-
- class cli_exception: odb::exception
- {
- public:
- virtual const char*
- what () const throw ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/sqlite/exceptions.hxx></code>
- header file to make these exceptions available in your application.</p>
-
- <p>The <code>odb::sqlite::forced_rollback</code> exception is thrown if
- SQLite is forcing the current transaction to roll back. For more
- information on this behavior refer to <a href="#18.5.6">Section 18.5.6,
- "Forced Rollback"</a>.</p>
-
- <p>The <code>odb::sqlite::database_exception</code> is thrown if
- an SQLite database operation fails. The SQLite-specific error
- information is accessible via the <code>error()</code>,
- <code>extended_error()</code>, and <code>message()</code> functions.
- All this information is also combined and returned in a
- human-readable form by the <code>what()</code> function.</p>
-
- <p>The <code>odb::sqlite::cli_exception</code> is thrown by the
- command line parsing constructor of the <code>odb::sqlite::database</code>
- class if the SQLite option values are missing or invalid. The
- <code>what()</code> function returns a human-readable description
- of an error.</p>
-
-
- <h2><a name="18.5">18.5 SQLite Limitations</a></h2>
-
- <p>The following sections describe SQLite-specific limitations imposed by
- the current SQLite and ODB runtime versions.</p>
-
- <h3><a name="18.5.1">18.5.1 Query Result Caching</a></h3>
-
- <p>SQLite ODB runtime implementation does not perform query result caching
- (<a href="#4.4">Section 4.4, "Query Result"</a>) even when explicitly
- requested. The SQLite API supports interleaving execution of multiple
- prepared statements on a single connection. As a result, with SQLite, it
- is possible to have multiple uncached results and calls to other database
- functions do not invalidate them. The only limitation of the uncached
- SQLite results is the unavailability of the <code>result::size()</code>
- function. If you call this function on an SQLite query result, then
- the <code>odb::result_not_cached</code> exception
- (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>) is always
- thrown. Future versions of the SQLite ODB runtime library may add support
- for result caching.</p>
-
- <h3><a name="18.5.2">18.5.2 Automatic Assignment of Object Ids</a></h3>
-
- <p>Due to SQLite API limitations, every automatically assigned object id
- (<a href="#14.4.2">Section 14.4.2, "<code>auto</code>"</a>) should have
- the <code>INTEGER</code> SQLite type. While SQLite will treat other
- integer type names (such as <code>INT</code>, <code>BIGINT</code>, etc.)
- as <code>INTEGER</code>, automatic id assignment will not work. By default,
- ODB maps all C++ integral types to <code>INTEGER</code>. This means that
- the only situation that requires consideration is the assignment of a
- custom database type using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>). For
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- //#pragma db id auto type("INT") // Will not work.
- //#pragma db id auto type("INTEGER") // Ok.
- #pragma db id auto // Ok, Mapped to INTEGER.
- unsigned int id_;
-};
- </pre>
-
- <h3><a name="18.5.3">18.5.3 Foreign Key Constraints</a></h3>
-
- <p>By default the SQLite ODB runtime enables foreign key constraints
- checking (<code>PRAGMA foreign_keys=ON</code>). You can disable foreign
- keys by passing <code>false</code> as the <code>foreign_keys</code>
- argument to one of the <code>odb::sqlite::database</code> constructors.
- Foreign keys will also be disabled if the SQLite library is built without
- support for foreign keys (<code>SQLITE_OMIT_FOREIGN_KEY</code> and
- <code>SQLITE_OMIT_TRIGGER</code> macros) or if you are using
- an SQLite version prior to 3.6.19, which does not support foreign
- key constraints checking.</p>
-
- <p>If foreign key constraints checking is disabled or not available,
- then inconsistencies in object relationships will not be detected.
- Furthermore, using the <code>erase_query()</code> function
- (<a href="#3.11">Section 3.11, "Deleting Persistent Objects"</a>)
- to delete persistent objects that contain containers will not work
- correctly. Container data for such objects will not be deleted.</p>
-
- <p>When foreign key constraints checking is enabled, then you may
- get the "foreign key constraint failed" error while re-creating the
- database schema. This error is due to bugs in the SQLite DDL foreign
- keys support. The recommended work-around for this problem is to
- temporarily disable foreign key constraints checking while
- re-creating the schema. The following code fragment shows how
- this can be done:</p>
-
- <pre class="cxx">
-#include &lt;odb/connection.hxx>
-#include &lt;odb/transaction.hxx>
-#include &lt;odb/schema-catalog.hxx>
-
-odb::database&amp; db = ...
-
-{
- odb::connection_ptr c (db.connection ());
-
- c->execute ("PRAGMA foreign_keys=OFF");
-
- odb::transaction t (c->begin ());
- odb::schema_catalog::create_schema (db);
- t.commit ();
-
- c->execute ("PRAGMA foreign_keys=ON");
-}
- </pre>
-
- <p>Finally, ODB assumes the standard SQL behavior which requires
- that foreign key constraints checking is deferred until the
- transaction is committed. Default SQLite behavior is to check such
- constraints immediately. As a result, when used with ODB, a custom
- database schema that defines foreign key constraints may need to
- declare such constraints as <code>DEFERRABLE INITIALLY DEFERRED</code>,
- as shown in the following example. By default, schemas generated by
- the ODB compiler meet this requirement automatically.</p>
-
- <pre class="sql">
-CREATE TABLE Employee (
- ...
- employer INTEGER REFERENCES Employer(id)
- DEFERRABLE INITIALLY DEFERRED);
- </pre>
-
- <p>You can override the default behavior and instruct the ODB
- compiler to generate non-deferrable foreign keys by specifying
- the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
- compiler option. Note, however, that in this case the order in
- which you persist, update, and erase objects within a transaction
- becomes important.</p>
-
- <h3><a name="18.5.4">18.5.4 Constraint Violations</a></h3>
-
- <p>Due to the granularity of the SQLite error codes, it is impossible
- to distinguish between the duplicate primary key and other constraint
- violations. As a result, when making an object persistent, the SQLite
- ODB runtime will translate all constraint violation errors to the
- <code>object_already_persistent</code> exception (<a href="#3.14">Section
- 3.14, "ODB Exceptions"</a>).</p>
-
- <h3><a name="18.5.5">18.5.5 Sharing of Queries</a></h3>
-
- <p>As discussed in <a href="#4.3">Section 4.3, "Executing a Query"</a>, a
- query instance that does not have any by-reference parameters is
- immutable and can be shared between multiple threads without
- synchronization. Currently, the SQLite ODB runtime does not support this
- functionality. Future versions of the library will remove this
- limitation.</p>
-
- <h3><a name="18.5.6">18.5.6 Forced Rollback</a></h3>
-
- <p>In SQLite 3.7.11 or later, if one of the connections participating in
- the shared cache rolls back a transaction, then ongoing transactions
- on other connections in the shared cache may also be forced to roll back.
- An example of such behavior would be a read-only transaction that is
- forced to roll back while iterating over the query result because another
- transaction on another connection was rolled back.</p>
-
- <p>If a transaction is being forced to roll back by SQLite, then ODB
- throws <code>odb::sqlite::forced_rollback</code>
- (<a href="#18.4">Section 18.4, "SQLite Exceptions"</a>) which is
- a recoverable exception (<a href="#3.7">3.7 Error Handling and
- Recovery</a>). As a result, the recommended way to handle this
- exception is to re-execute the affected transaction.</p>
-
- <h3><a name="18.5.7">18.5.7 Database Schema Evolution</a></h3>
-
- <p>From the list of schema migration changes supported by ODB
- (<a href="#13.2">Section 13.2, "Schema Migration"</a>), the
- following are not supported by SQLite:</p>
-
- <ul class="list">
- <li>drop column</li>
- <li>alter column, set <code>NULL</code>/<code>NOT NULL</code></li>
- <li>add foreign key</li>
- <li>drop foreign key</li>
- </ul>
-
- <p>The biggest problem is the lack of support for dropping columns.
- This means that it would be impossible to delete a data member
- in a persistent class. To work around this limitation ODB
- implements <em>logical delete</em> for columns that allow
- <code>NULL</code> values. In this case, instead of dropping
- the column (in the post-migration stage), the schema migration
- statements will automatically reset this column in all the
- existing rows to <code>NULL</code>. Any new rows that are
- inserted later will also automatically have this column set
- to <code>NULL</code> (unless the column specifies a default
- value).</p>
-
- <p>Since it is also impossible to change the column's
- <code>NULL</code>/<code>NOT NULL</code> attribute after it
- has been added, to make schema evolution support usable in
- SQLite, all the columns should be added as <code>NULL</code>
- even if semantically they should not allow <code>NULL</code>
- values. We should also normally refrain from assigning
- default values to columns (<a href="#14.4.7">Section 14.4.7,
- <code>default</code></a>), unless the space overhead of
- a default value is not a concern. Explicitly making all
- the data members <code>NULL</code> would be burdensome
- and ODB provides the <code>--sqlite-override-null</code>
- command line option that forces all the columns, even those
- that were explicitly marked <code>NOT NULL</code>, to be
- <code>NULL</code> in SQLite.</p>
-
- <p>SQLite only supports adding foreign keys as part of the
- column addition. As a result, we can only add a new
- data member of an object pointer type if it points
- to an object with a simple (single-column) object id.</p>
-
- <p>SQLite also doesn't support dropping foreign keys.
- Leaving a foreign key around works well with logical
- delete unless we also want to delete the pointed-to
- object. In this case we will have to leave an
- empty table corresponding to the pointed-to object
- around. An alternative would be to make a copy of the
- pointing object without the object pointer, migrate the
- data, and then delete both the old pointing and the
- pointed-to objects. Since this will result in dropping
- the pointing table, the foreign key will be dropped
- as well. Yet another, more radical, solution to this
- problem is to disable foreign keys checking altogether
- (see the <code>foreign_keys</code> SQLite pragma).</p>
-
- <p>To summarize, to make schema evolution support usable
- in SQLite we should pass the <code>--sqlite-override-null</code>
- option when compiling our persistent classes and also refrain
- from assigning default values to data members. Note also that
- this has to be done from the start so that every column is added
- as <code>NULL</code> and therefore can be logically deleted later.
- In particular, you cannot add the <code>--sqlite-override-null</code>
- option when you realize you need to delete a data member. At this
- point it is too late since the column has already been added
- as <code>NOT NULL</code> in existing databases. We should also
- avoid composite object ids if we are planning to use object
- relationships.</p>
-
- <h2><a name="18.6">18.6 SQLite Index Definitions</a></h2>
-
- <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
- "Index Definition Pragmas"</a>) is used to define an SQLite index,
- the <code>type</code> clause specifies the index type (for example,
- <code>UNIQUE</code>) while the <code>method</code> and
- <code>options</code> clauses are not used. The column options
- can be used to specify collations and the sort order. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- std::string name_;
-
- #pragma db index member(name_, "COLLATE binary DESC")
-};
- </pre>
-
- <p>Index names in SQLite are database-global. To avoid name clashes,
- ODB automatically prefixes each index name with the table name on
- which it is defined.</p>
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="19">19 PostgreSQL Database</a></h1>
-
- <p>To generate support code for the PostgreSQL database you will need
- to pass the "<code>--database&nbsp;pgsql</code>"
- (or "<code>-d&nbsp;pgsql</code>") option to the ODB compiler.
- Your application will also need to link to the PostgreSQL ODB runtime
- library (<code>libodb-pgsql</code>). All PostgreSQL-specific ODB
- classes are defined in the <code>odb::pgsql</code> namespace.</p>
-
- <p>ODB utilizes prepared statements extensively. Support for prepared
- statements was added in PostgreSQL version 7.4 with the introduction
- of the messaging protocol version 3.0. For this reason, ODB supports
- only PostgreSQL version 7.4 and later.</p>
-
- <h2><a name="19.1">19.1 PostgreSQL Type Mapping</a></h2>
-
- <p>The following table summarizes the default mapping between basic
- C++ value types and PostgreSQL database types. This mapping can be
- customized on the per-type and per-member basis using the ODB
- Pragma Language (<a href="#14">Chapter 14, "ODB Pragma
- Language"</a>).</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>C++ Type</th>
- <th>PostgreSQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>bool</code></td>
- <td><code>BOOLEAN</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char</code></td>
- <td><code>CHAR(1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>signed char</code></td>
- <td><code>SMALLINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned char</code></td>
- <td><code>SMALLINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>short</code></td>
- <td><code>SMALLINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned short</code></td>
- <td><code>SMALLINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>int</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned int</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>float</code></td>
- <td><code>REAL</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>double</code></td>
- <td><code>DOUBLE PRECISION</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>std::string</code></td>
- <td><code>TEXT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char[N]</code></td>
- <td><code>VARCHAR(N-1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
- </table>
-
- <p>It is possible to map the <code>char</code> C++ type to an integer
- database type (for example, <code>SMALLINT</code>) using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>).</p>
-
- <p>Additionally, by default, C++ enums and C++11 enum classes are
- automatically mapped to the PostgreSQL types corresponding to their
- underlying integral types (see table above). The default
- <code>NULL</code> semantics is <code>NOT NULL</code>. For
- example:</p>
-
- <pre class="cxx">
-enum color {red, green, blue};
-enum class taste: unsigned char
-{
- bitter = 1,
- sweet,
- sour = 4,
- salty
-};
-
-#pragma db object
-class object
-{
- ...
-
- color color_; // Automatically mapped to INTEGER.
- taste taste_; // Automatically mapped to SMALLINT.
-};
- </pre>
-
- <p>Note also that because PostgreSQL does not support unsigned integers,
- the <code>unsigned&nbsp;short</code>, <code>unsigned&nbsp;int</code>, and
- <code>unsigned&nbsp;long</code>/<code>unsigned&nbsp;long&nbsp;long</code> C++ types
- are by default mapped to the <code>SMALLINT</code>, <code>INTEGER</code>,
- and <code>BIGINT</code> PostgreSQL types, respectively. The sign bit
- of the value stored by the database for these types will contain
- the most significant bit of the actual unsigned value being
- persisted.</p>
-
- <p>It is also possible to add support for additional PostgreSQL types,
- such as <code>NUMERIC</code>, geometry types, <code>XML</code>,
- <code>JSON</code>, enumeration types, composite types, arrays,
- geospatial types, and the key-value store (<code>HSTORE</code>).
- For more information, refer to <a href="#14.8">Section 14.8,
- "Database Type Mapping Pragmas"</a>.</p>
-
- <h3><a name="19.1.1">19.1.1 String Type Mapping</a></h3>
-
- <p>The PostgreSQL ODB runtime library provides support for mapping the
- <code>std::string</code>, <code>char[N]</code>, and
- <code>std::array&lt;char, N></code> types to the PostgreSQL
- <code>CHAR</code>, <code>VARCHAR</code>, and <code>TEXT</code>
- types. However, these mappings are not enabled by default (in
- particular, by default, <code>std::array</code> will be treated
- as a container). To enable the alternative mappings for these
- types we need to specify the database type explicitly using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("CHAR(2)")
- char state_[2];
-
- #pragma db type("VARCHAR(128)")
- std::string name_;
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-#pragma db value(std::string) type("VARCHAR(128)")
-
-#pragma db object
-class object
-{
- ...
-
- std::string name_; // Mapped to VARCHAR(128).
-};
- </pre>
-
- <p>The <code>char[N]</code> and <code>std::array&lt;char, N></code> values
- may or may not be zero-terminated. When extracting such values from the
- database, ODB will append the zero terminator if there is enough
- space.</p>
-
- <h3><a name="19.1.2">19.1.2 Binary Type and <code>UUID</code> Mapping</a></h3>
-
- <p>The PostgreSQL ODB runtime library provides support for mapping the
- <code>std::vector&lt;char></code>,
- <code>std::vector&lt;unsigned&nbsp;char></code>,
- <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
- <code>std::array&lt;char, N></code>, and
- <code>std::array&lt;unsigned char, N></code> types to the PostgreSQL
- <code>BYTEA</code> type. There is also support for mapping the
- <code>char[16]</code> array to the PostgreSQL <code>UUID</code> type.
- However, these mappings are not enabled by default (in particular, by
- default, <code>std::vector</code> and <code>std::array</code> will be
- treated as containers). To enable the alternative mappings for these
- types we need to specify the database type explicitly using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("UUID")
- char uuid_[16];
-
- #pragma db type("BYTEA")
- std::vector&lt;char> buf_;
-
- #pragma db type("BYTEA")
- unsigned char data_[256];
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;char> buffer;
-#pragma db value(buffer) type("BYTEA")
-
-#pragma db object
-class object
-{
- ...
-
- buffer buf_; // Mapped to BYTEA.
-};
- </pre>
-
- <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
- the Database"</a>) <code>char[N]</code> and
- <code>std::array&lt;char, N></code> parameters are by default passed
- as a string rather than a binary. To pass such parameters as a binary,
- we need to specify the database type explicitly in the
- <code>_val()</code>/<code>_ref()</code> calls. Note also that we
- don't need to do this for the integrated queries, for example:</p>
-
- <pre class="cxx">
-char u[16] = {...};
-
-db.query&lt;object> ("uuid = " + query::_val&lt;odb::pgsql::id_uuid> (u));
-db.query&lt;object> ("buf = " + query::_val&lt;odb::pgsql::id_bytea> (u));
-db.query&lt;object> (query::uuid == query::_ref (u));
- </pre>
-
- <h2><a name="19.2">19.2 PostgreSQL Database Class</a></h2>
-
- <p>The PostgreSQL <code>database</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace pgsql
- {
- class database: public odb::database
- {
- public:
- database (const std::string&amp; user,
- const std::string&amp; password,
- const std::string&amp; db,
- const std::string&amp; host = "",
- unsigned int port = 0,
- const std::string&amp; extra_conninfo = "",
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; user,
- const std::string&amp; password,
- const std::string&amp; db,
- const std::string&amp; host,
- const std::string&amp; socket_ext,
- const std::string&amp; extra_conninfo = "",
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; conninfo,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (int&amp; argc,
- char* argv[],
- bool erase = false,
- const std::string&amp; extra_conninfo = "",
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- static void
- print_usage (std::ostream&amp;);
-
- public:
- const std::string&amp;
- user () const;
-
- const std::string&amp;
- password () const;
-
- const std::string&amp;
- db () const;
-
- const std::string&amp;
- host () const;
-
- unsigned int
- port () const;
-
- const std::string&amp;
- socket_ext () const;
-
- const std::string&amp;
- extra_conninfo () const;
-
- const std::string&amp;
- conninfo () const;
-
- public:
- connection_ptr
- connection ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/pgsql/database.hxx></code>
- header file to make this class available in your application.</p>
-
- <p>The overloaded <code>database</code> constructors allow us to specify
- the PostgreSQL database parameters that should be used when connecting
- to the database. The <code>port</code> argument in the first constructor
- is an integer value specifying the TCP/IP port number to connect to. A
- zero port number indicates that the default port should be used.
- The <code>socket_ext</code> argument in the second constructor is a
- string value specifying the UNIX-domain socket file name extension.</p>
-
- <p>The third constructor allows us to specify all the database parameters
- as a single <code>conninfo</code> string. All other constructors
- accept additional database connection parameters as the
- <code>extra_conninfo</code> argument. For more information
- about the format of the <code>conninfo</code> string, refer to
- the <code>PQconnectdb()</code> function description in the PostgreSQL
- documentation. In the case of <code>extra_conninfo</code>, all the
- database parameters provided in this string will take precedence
- over those explicitly specified with other constructor arguments.</p>
-
- <p>The last constructor extracts the database parameters
- from the command line. The following options are recognized:</p>
-
- <pre class="terminal">
- --user &lt;login> | --username &lt;login>
- --password &lt;password>
- --database &lt;name> | --dbname &lt;name>
- --host &lt;host>
- --port &lt;integer>
- --options-file &lt;file>
- </pre>
-
- <p>The <code>--options-file</code> option allows us to specify some
- or all of the database options in a file with each option appearing
- on a separate line followed by a space and an option value.</p>
-
- <p>If the <code>erase</code> argument to this constructor is true,
- then the above options are removed from the <code>argv</code>
- array and the <code>argc</code> count is updated accordingly.
- This is primarily useful if your application accepts other
- options or arguments and you would like to get the PostgreSQL
- options out of the <code>argv</code> array.</p>
-
- <p>This constructor throws the <code>odb::pgsql::cli_exception</code>
- exception if the PostgreSQL option values are missing or invalid.
- See section <a href="#19.4">Section 19.4, "PostgreSQL Exceptions"</a>
- for more information on this exception.</p>
-
- <p>The static <code>print_usage()</code> function prints the list of options
- with short descriptions that are recognized by this constructor.</p>
-
- <p>The last argument to all of the constructors is a pointer to the
- connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
- in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
- non-<code>NULL</code> value, the database instance assumes ownership
- of the factory instance. The connection factory interface as well as
- the available implementations are described in the next section.</p>
-
- <p>The set of accessor functions following the constructors allows us
- to query the parameters of the <code>database</code> instance. Note that
- the <code>conninfo()</code> accessor returns a complete
- <code>conninfo</code> string which includes parameters that were
- explicitly specified with the various constructor arguments, as well as
- the extra parameters passed in the <code>extra_conninfo</code> argument.
- The <code>extra_conninfo()</code> accessor will return the
- <code>conninfo</code> string as passed in the <code>extra_conninfo</code>
- argument.</p>
-
- <p>The <code>connection()</code> function returns a pointer to the
- PostgreSQL database connection encapsulated by the
- <code>odb::pgsql::connection</code> class. For more information
- on <code>pgsql::connection</code>, refer to <a href="#19.3">Section
- 19.3, "PostgreSQL Connection and Connection Factory"</a>.</p>
-
- <h2><a name="19.3">19.3 PostgreSQL Connection and Connection Factory</a></h2>
-
- <p>The <code>pgsql::connection</code> class has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace pgsql
- {
- class connection: public odb::connection
- {
- public:
- connection (database&amp;);
- connection (database&amp;, PGconn*);
-
- PGconn*
- handle ();
- };
-
- typedef details::shared_ptr&lt;connection> connection_ptr;
- }
-}
- </pre>
-
- <p>For more information on the <code>odb::connection</code> interface,
- refer to <a href="#3.6">Section 3.6, "Connections"</a>. The first
- overloaded <code>pgsql::connection</code> constructor establishes a
- new PostgreSQL connection. The second constructor allows us to create
- a <code>connection</code> instance by providing an already connected
- native PostgreSQL handle. Note that the <code>connection</code>
- instance assumes ownership of this handle. The <code>handle()</code>
- accessor returns the PostgreSQL handle corresponding to the connection.</p>
-
- <p>The <code>pgsql::connection_factory</code> abstract class has the
- following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace pgsql
- {
- class connection_factory
- {
- public:
- virtual void
- database (database&amp;) = 0;
-
- virtual connection_ptr
- connect () = 0;
- };
- }
-}
- </pre>
-
- <p>The <code>database()</code> function is called when a connection
- factory is associated with a database instance. This happens in
- the <code>odb::pgsql::database</code> class constructors. The
- <code>connect()</code> function is called whenever a database
- connection is requested.</p>
-
- <p>The two implementations of the <code>connection_factory</code>
- interface provided by the PostgreSQL ODB runtime are
- <code>new_connection_factory</code> and
- <code>connection_pool_factory</code>. You will need to include
- the <code>&lt;odb/pgsql/connection-factory.hxx></code>
- header file to make the <code>connection_factory</code> interface
- and these implementation classes available in your application.</p>
-
- <p>The <code>new_connection_factory</code> class creates a new
- connection whenever one is requested. When a connection is no
- longer needed, it is released and closed. The
- <code>new_connection_factory</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace pgsql
- {
- class new_connection_factory: public connection_factory
- {
- public:
- new_connection_factory ();
- };
-};
- </pre>
-
- <p>The <code>connection_pool_factory</code> class implements a
- connection pool. It has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace pgsql
- {
- class connection_pool_factory: public connection_factory
- {
- public:
- connection_pool_factory (std::size_t max_connections = 0,
- std::size_t min_connections = 0);
-
- protected:
- class pooled_connection: public connection
- {
- public:
- pooled_connection (database_type&amp;);
- pooled_connection (database_type&amp;, PGconn*);
- };
-
- typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
-
- virtual pooled_connection_ptr
- create ();
- };
-};
- </pre>
-
- <p>The <code>max_connections</code> argument in the
- <code>connection_pool_factory</code> constructor specifies the maximum
- number of concurrent connections that this pool factory will
- maintain. Similarly, the <code>min_connections</code> argument
- specifies the minimum number of available connections that
- should be kept open.</p>
-
- <p>Whenever a connection is requested, the pool factory first
- checks if there is an unused connection that can be returned.
- If there is none, the pool factory checks the
- <code>max_connections</code> value to see if a new connection
- can be created. If the total number of connections maintained
- by the pool is less than this value, then a new connection is
- created and returned. Otherwise, the caller is blocked until
- a connection becomes available.</p>
-
- <p>When a connection is released, the pool factory first checks
- if there are blocked callers waiting for a connection. If so, then
- one of them is unblocked and is given the connection. Otherwise,
- the pool factory checks whether the total number of connections
- maintained by the pool is greater than the <code>min_connections</code>
- value. If that's the case, the connection is closed. Otherwise, the
- connection is added to the pool of available connections to be
- returned on the next request. In other words, if the number of
- connections maintained by the pool exceeds <code>min_connections</code>
- and there are no callers waiting for a new connection,
- the pool will close the excess connections.</p>
-
- <p>If the <code>max_connections</code> value is 0, then the pool will
- create a new connection whenever all of the existing connections
- are in use. If the <code>min_connections</code> value is 0, then
- the pool will never close a connection and instead maintain all
- the connections that were ever created.</p>
-
- <p>The <code>create()</code> virtual function is called whenever the
- pool needs to create a new connection. By deriving from the
- <code>connection_pool_factory</code> class and overriding this
- function we can implement custom connection establishment
- and configuration.</p>
-
- <p>If you pass <code>NULL</code> as the connection factory to one of the
- <code>database</code> constructors, then the
- <code>connection_pool_factory</code> instance will be created by default
- with the min and max connections values set to <code>0</code>. The
- following code fragment shows how we can pass our own connection factory
- instance:</p>
-
- <pre class="cxx">
-#include &lt;odb/database.hxx>
-
-#include &lt;odb/pgsql/database.hxx>
-#include &lt;odb/pgsql/connection-factory.hxx>
-
-int
-main (int argc, char* argv[])
-{
- auto_ptr&lt;odb::pgsql::connection_factory> f (
- new odb::pgsql::connection_pool_factory (20));
-
- auto_ptr&lt;odb::database> db (
- new pgsql::database (argc, argv, false, "", f));
-}
- </pre>
-
- <h2><a name="19.4">19.4 PostgreSQL Exceptions</a></h2>
-
- <p>The PostgreSQL ODB runtime library defines the following
- PostgreSQL-specific exceptions:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace pgsql
- {
- class database_exception: odb::database_exception
- {
- public:
- const std::string&amp;
- message () const;
-
- const std::string&amp;
- sqlstate () const;
-
- virtual const char*
- what () const throw ();
- };
-
- class cli_exception: odb::exception
- {
- public:
- virtual const char*
- what () const throw ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/pgsql/exceptions.hxx></code>
- header file to make these exceptions available in your application.</p>
-
- <p>The <code>odb::pgsql::database_exception</code> is thrown if
- a PostgreSQL database operation fails. The PostgreSQL-specific error
- information is accessible via the <code>message()</code> and
- <code>sqlstate()</code> functions. All this information is also
- combined and returned in a human-readable form by the <code>what()</code>
- function.</p>
-
- <p>The <code>odb::pgsql::cli_exception</code> is thrown by the
- command line parsing constructor of the <code>odb::pgsql::database</code>
- class if the PostgreSQL option values are missing or invalid. The
- <code>what()</code> function returns a human-readable description
- of an error.</p>
-
- <h2><a name="19.5">19.5 PostgreSQL Limitations</a></h2>
-
- <p>The following sections describe PostgreSQL-specific limitations imposed
- by the current PostgreSQL and ODB runtime versions.</p>
-
- <h3><a name="19.5.1">19.5.1 Query Result Caching</a></h3>
-
- <p>The PostgreSQL ODB runtime implementation will always return a
- cached query result (<a href="#4.4">Section 4.4, "Query Result"</a>)
- even when explicitly requested not to. This is a limitation of the
- PostgreSQL client library (<code>libpq</code>) which does not
- support uncached (streaming) query results.</p>
-
- <h3><a name="19.5.2">19.5.2 Foreign Key Constraints</a></h3>
-
- <p>ODB assumes the standard SQL behavior which requires that
- foreign key constraints checking is deferred until the
- transaction is committed. Default PostgreSQL behavior is
- to check such constraints immediately. As a result, when
- used with ODB, a custom database schema that defines foreign
- key constraints may need to declare such constraints as
- <code>INITIALLY DEFERRED</code>, as shown in the following example.
- By default, schemas generated by the ODB compiler meet this requirement
- automatically.</p>
-
- <pre class="sql">
-CREATE TABLE Employee (
- ...
- employer BIGINT REFERENCES Employer(id) INITIALLY DEFERRED);
- </pre>
-
- <p>You can override the default behavior and instruct the ODB
- compiler to generate non-deferrable foreign keys by specifying
- the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
- compiler option. Note, however, that in this case the order in
- which you persist, update, and erase objects within a transaction
- becomes important.</p>
-
- <h3><a name="19.5.3">19.5.3 Unique Constraint Violations</a></h3>
-
- <p>Due to the granularity of the PostgreSQL error codes, it is impossible
- to distinguish between the duplicate primary key and other unique
- constraint violations. As a result, when making an object persistent,
- the PostgreSQL ODB runtime will translate all unique constraint violation
- errors to the <code>object_already_persistent</code> exception
- (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>).</p>
-
- <h3><a name="19.5.4">19.5.4 Date-Time Format</a></h3>
-
- <p>ODB expects the PostgreSQL server to use integers as a binary
- format for the date-time types, which is the default for most
- PostgreSQL configurations. When creating a connection, ODB
- examines the <code>integer_datetimes</code> PostgreSQL server
- parameter and if it is <code>false</code>,
- <code>odb::pgsql::database_exception</code> is thrown. You may
- check the value of this parameter for your server by executing
- the following SQL query:</p>
-
- <pre class="sql">
-SHOW integer_datetimes
- </pre>
-
- <h3><a name="19.5.5">19.5.5 Timezones</a></h3>
-
- <p>ODB does not currently natively support the PostgreSQL date-time types
- with timezone information. However, these types can be accessed by
- mapping them to one of the natively supported types, as discussed
- in <a href="#14.8">Section 14.8, "Database Type Mapping Pragmas"</a>.</p>
-
- <h3><a name="19.5.6">19.5.6 <code>NUMERIC</code> Type Support</a></h3>
-
- <p>Support for the PostgreSQL <code>NUMERIC</code> type is limited
- to providing a binary buffer containing the binary representation
- of the value. For more information on the binary format used to
- store <code>NUMERIC</code> values refer to the PostgreSQL
- documentation. An alternative approach to accessing <code>NUMERIC</code>
- values is to map this type to one of the natively supported
- ones, as discussed in <a href="#14.8">Section 14.8, "Database
- Type Mapping Pragmas"</a>.</p>
-
- <h3><a name="19.5.7">19.5.7 Bulk Operations Support</a></h3>
-
- <p>Support for bulk operations (<a href="#15.3">Section 15.3, "Bulk
- Database Operations"</a>) requires PostgreSQL client library
- (<code>libpq</code>) version 14 or later and PostgreSQL server
- version 7.4 or later.</p>
-
-
- <h2><a name="19.6">19.6 PostgreSQL Index Definitions</a></h2>
-
- <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
- "Index Definition Pragmas"</a>) is used to define a PostgreSQL index,
- the <code>type</code> clause specifies the index type (for example,
- <code>UNIQUE</code>), the <code>method</code> clause specifies the
- index method (for example, <code>BTREE</code>, <code>HASH</code>,
- <code>GIN</code>, etc.), and the <code>options</code> clause
- specifies additional index options, such as storage parameters,
- table spaces, and the <code>WHERE</code> predicate. To support
- the definition of concurrent indexes, the <code>type</code>
- clause can end with the word <code>CONCURRENTLY</code> (upper and
- lower cases are recognized). The column options can be used to
- specify collations, operator classes, and the sort order. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- std::string name_;
-
- #pragma db index \
- type("UNIQUE CONCURRENTLY") \
- method("HASH") \
- member(name_, "DESC") \
- options("WITH(FILLFACTOR = 80)")
-};
- </pre>
-
- <p>Index names in PostgreSQL are schema-global. To avoid name clashes,
- ODB automatically prefixes each index name with the table name on
- which it is defined.</p>
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="20">20 Oracle Database</a></h1>
-
- <p>To generate support code for the Oracle database you will need
- to pass the "<code>--database&nbsp;oracle</code>"
- (or "<code>-d&nbsp;oracle</code>") option to the ODB compiler.
- Your application will also need to link to the Oracle ODB runtime
- library (<code>libodb-oracle</code>). All Oracle-specific ODB
- classes are defined in the <code>odb::oracle</code> namespace.</p>
-
- <h2><a name="20.1">20.1 Oracle Type Mapping</a></h2>
-
- <p>The following table summarizes the default mapping between basic
- C++ value types and Oracle database types. This mapping can be
- customized on the per-type and per-member basis using the ODB
- Pragma Language (<a href="#14">Chapter 14, "ODB Pragma
- Language"</a>).</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>C++ Type</th>
- <th>Oracle Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>bool</code></td>
- <td><code>NUMBER(1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char</code></td>
- <td><code>CHAR(1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>signed char</code></td>
- <td><code>NUMBER(3)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned char</code></td>
- <td><code>NUMBER(3)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>short</code></td>
- <td><code>NUMBER(5)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned short</code></td>
- <td><code>NUMBER(5)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>int</code></td>
- <td><code>NUMBER(10)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned int</code></td>
- <td><code>NUMBER(10)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long</code></td>
- <td><code>NUMBER(19)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long</code></td>
- <td><code>NUMBER(20)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long long</code></td>
- <td><code>NUMBER(19)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long long</code></td>
- <td><code>NUMBER(20)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>float</code></td>
- <td><code>BINARY_FLOAT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>double</code></td>
- <td><code>BINARY_DOUBLE</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>std::string</code></td>
- <td><code>VARCHAR2(512)</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char[N]</code></td>
- <td><code>VARCHAR2(N-1)</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>It is possible to map the <code>char</code> C++ type to an integer
- database type (for example, <code>NUMBER(3)</code>) using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>).</p>
-
- <p>In Oracle empty <code>VARCHAR2</code> and <code>NVARCHAR2</code>
- strings are represented as a <code>NULL</code> value. As a result,
- columns of the <code>std::string</code> and <code>char[N]</code>
- types are by default declared as <code>NULL</code> except for
- primary key columns. However, you can override this by explicitly
- declaring such columns as <code>NOT NULL</code> with the
- <code>db&nbsp;not_null</code> pragma (<a href="#14.4.6">Section
- 14.4.6, "<code>null/not_null</code>"</a>). This also means that for
- object ids that are mapped to these Oracle types, an empty string is
- an invalid value.</p>
-
- <p>Additionally, by default, C++ enums and C++11 enum classes are
- automatically mapped to the Oracle types corresponding to their
- underlying integral types (see table above). The default
- <code>NULL</code> semantics is <code>NOT NULL</code>. For
- example:</p>
-
- <pre class="cxx">
-enum color {red, green, blue};
-enum class taste: unsigned char
-{
- bitter = 1,
- sweet,
- sour = 4,
- salty
-};
-
-#pragma db object
-class object
-{
- ...
-
- color color_; // Automatically mapped to NUMBER(10).
- taste taste_; // Automatically mapped to NUMBER(3).
-};
- </pre>
-
- <p>It is also possible to add support for additional Oracle types,
- such as <code>XML</code>, geospatial types, user-defined types,
- and collections (arrays, table types). For more information, refer to
- <a href="#14.8">Section 14.8, "Database Type Mapping
- Pragmas"</a>.</p>
-
- <h3><a name="20.1.1">20.1.1 String Type Mapping</a></h3>
-
- <p>The Oracle ODB runtime library provides support for mapping the
- <code>std::string</code>, <code>char[N]</code>, and
- <code>std::array&lt;char, N></code> types to the Oracle <code>CHAR</code>,
- <code>VARCHAR2</code>, <code>CLOB</code>, <code>NCHAR</code>,
- <code>NVARCHAR2</code>, and <code>NCLOB</code> types. However,
- these mappings are not enabled by default (in particular, by
- default, <code>std::array</code> will be treated as a container).
- To enable the alternative mappings for these types we need to
- specify the database type explicitly using the <code>db&nbsp;type</code>
- pragma (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>),
- for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type ("CHAR(2)")
- char state_[2];
-
- #pragma db type ("VARCHAR(128)") null
- std::string name_;
-
- #pragma db type ("CLOB")
- std::string text_;
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-#pragma db value(std::string) type("VARCHAR(128)") null
-
-#pragma db object
-class object
-{
- ...
-
- std::string name_; // Mapped to VARCHAR(128).
-
- #pragma db type ("CLOB")
- std::string text_; // Mapped to CLOB.
-};
- </pre>
-
- <p>The <code>char[N]</code> and <code>std::array&lt;char, N></code> values
- may or may not be zero-terminated. When extracting such values from the
- database, ODB will append the zero terminator if there is enough
- space.</p>
-
- <h3><a name="20.1.2">20.1.2 Binary Type Mapping</a></h3>
-
- <p>The Oracle ODB runtime library provides support for mapping the
- <code>std::vector&lt;char></code>,
- <code>std::vector&lt;unsigned&nbsp;char></code>,
- <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
- <code>std::array&lt;char, N></code>, and
- <code>std::array&lt;unsigned char, N></code>
- types to the Oracle <code>BLOB</code> and <code>RAW</code> types.
- However, these mappings are not enabled by default (in particular, by
- default, <code>std::vector</code> and <code>std::array</code> will be
- treated as containers). To enable the alternative mappings for these
- types we need to specify the database type explicitly using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("BLOB")
- std::vector&lt;char> buf_;
-
- #pragma db type("RAW(16)")
- unsigned char uuid_[16];
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;char> buffer;
-#pragma db value(buffer) type("BLOB")
-
-#pragma db object
-class object
-{
- ...
-
- buffer buf_; // Mapped to BLOB.
-};
- </pre>
-
- <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
- the Database"</a>) <code>char[N]</code> and
- <code>std::array&lt;char, N></code> parameters are by default passed
- as a string rather than a binary. To pass such parameters as a binary,
- we need to specify the database type explicitly in the
- <code>_val()</code>/<code>_ref()</code> calls. Note also that we
- don't need to do this for the integrated queries, for example:</p>
-
- <pre class="cxx">
-char u[16] = {...};
-
-db.query&lt;object> ("uuid = " + query::_val&lt;odb::oracle::id_raw> (u));
-db.query&lt;object> (query::uuid == query::_ref (u));
- </pre>
-
- <h2><a name="20.2">20.2 Oracle Database Class</a></h2>
-
- <p>The Oracle <code>database</code> class encapsulates the OCI environment
- handle as well as the database connection string and user credentials
- that are used to establish connections to the database. It has the
- following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace oracle
- {
- class database: public odb::database
- {
- public:
- database (const std::string&amp; user,
- const std::string&amp; password,
- const std::string&amp; db,
- ub2 charset = 0,
- ub2 ncharset = 0,
- OCIEnv* environment = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; user,
- const std::string&amp; password,
- const std::string&amp; service,
- const std::string&amp; host,
- unsigned int port = 0,
- ub2 charset = 0,
- ub2 ncharset = 0,
- OCIEnv* environment = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (int&amp; argc,
- char* argv[],
- bool erase = false,
- ub2 charset = 0,
- ub2 ncharset = 0,
- OCIEnv* environment = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- static void
- print_usage (std::ostream&amp;);
-
- public:
- const std::string&amp;
- user () const;
-
- const std::string&amp;
- password () const;
-
- const std::string&amp;
- db () const;
-
- const std::string&amp;
- service () const;
-
- const std::string&amp;
- host () const;
-
- unsigned int
- port () const;
-
- ub2
- charset () const;
-
- ub2
- ncharset () const;
-
- OCIEnv*
- environment ();
-
- public:
- connection_ptr
- connection ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/oracle/database.hxx></code>
- header file to make this class available in your application.</p>
-
- <p>The overloaded <code>database</code> constructors allow us to specify the
- Oracle database parameters that should be used when connecting to the
- database. The <code>db</code> argument in the first constructor is a
- connection identifier that specifies the database to connect to. For more
- information on the format of the connection identifier, refer to the
- Oracle documentation.</p>
-
- <p>The second constructor allows us to specify the individual components
- of a connection identifier as the <code>service</code>, <code>host</code>,
- and <code>port</code> arguments. If the <code>host</code> argument is
- empty, then localhost is used by default. Similarly, if the
- <code>port</code> argument is zero, then the default port is used.</p>
-
- <p>The last constructor extracts the database parameters
- from the command line. The following options are recognized:</p>
-
- <pre class="terminal">
- --user &lt;login>
- --password &lt;password>
- --database &lt;connect-id>
- --service &lt;name>
- --host &lt;host>
- --port &lt;integer>
- --options-file &lt;file>
- </pre>
-
- <p>The <code>--options-file</code> option allows us to specify some
- or all of the database options in a file with each option appearing
- on a separate line followed by a space and an option value. Note that it
- is invalid to specify the <code>--database</code> option
- together with <code>--service</code>, <code>--host</code>, or
- <code>--port</code> options.</p>
-
- <p>If the <code>erase</code> argument to this constructor is true,
- then the above options are removed from the <code>argv</code>
- array and the <code>argc</code> count is updated accordingly.
- This is primarily useful if your application accepts other
- options or arguments and you would like to get the Oracle
- options out of the <code>argv</code> array.</p>
-
- <p>This constructor throws the <code>odb::oracle::cli_exception</code>
- exception if the Oracle option values are missing or invalid. See section
- <a href="#20.4">Section 20.4, "Oracle Exceptions"</a> for more
- information on this exception.</p>
-
- <p>The static <code>print_usage()</code> function prints the list of options
- with short descriptions that are recognized by this constructor.</p>
-
- <p>Additionally, all the constructors have the <code>charset</code>,
- <code>ncharset</code>, and <code>environment</code> arguments.
- The <code>charset</code> argument specifies the client-side database
- character encoding. Character data corresponding to the <code>CHAR</code>,
- <code>VARCHAR2</code>, and <code>CLOB</code> types will be delivered
- to and received from the application in this encoding. Similarly,
- the <code>ncharset</code> argument specifies the client-side national
- character encoding. Character data corresponding to the <code>NCHAR</code>,
- <code>NVARCHAR2</code>, and <code>NCLOB</code> types will be delivered
- to and received from the application in this encoding. For the complete
- list of available character encoding values, refer to the Oracle
- documentation. Commonly used encoding values are <code>873</code>
- (UTF-8), <code>31</code> (ISO-8859-1), and <code>1000</code> (UTF-16).
- If the database character encoding is not specified, then the
- <code>NLS_LANG</code> environment/registry variable is used. Similarly,
- if the national character encoding is not specified, then the
- <code>NLS_NCHAR</code> environment/registry variable is used. For more
- information on character encodings, refer to the
- <code>OCIEnvNlsCreate()</code> function in the Oracle Call Interface
- (OCI) documentation.</p>
-
- <p>The <code>environment</code> argument allows us to provide a custom
- OCI environment handle. If this argument is not <code>NULL</code>,
- then the passed handle is used in all the OCI function calls made
- by this <code>database</code> class instance. Note also that the
- <code>database</code> instance does not assume ownership of the
- passed environment handle and this handle should be valid for
- the lifetime of the <code>database</code> instance. If a custom
- environment handle is used, then the <code>charset</code> and
- <code>ncharset</code> arguments have no effect.</p>
-
- <p>The last argument to all of the constructors is a pointer to the
- connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
- in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
- non-<code>NULL</code> value, the database instance assumes ownership
- of the factory instance. The connection factory interface as well as
- the available implementations are described in the next section.</p>
-
- <p>The set of accessor functions following the constructors allows us
- to query the parameters of the <code>database</code> instance.</p>
-
- <p>The <code>connection()</code> function returns a pointer to the
- Oracle database connection encapsulated by the
- <code>odb::oracle::connection</code> class. For more information
- on <code>oracle::connection</code>, refer to <a href="#20.3">Section
- 20.3, "Oracle Connection and Connection Factory"</a>.</p>
-
- <h2><a name="20.3">20.3 Oracle Connection and Connection Factory</a></h2>
-
- <p>The <code>oracle::connection</code> class has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace oracle
- {
- class connection: public odb::connection
- {
- public:
- connection (database&amp;);
- connection (database&amp;, OCISvcCtx*);
-
- OCISvcCtx*
- handle ();
-
- OCIError*
- error_handle ();
-
- details::buffer&amp;
- lob_buffer ();
- };
-
- typedef details::shared_ptr&lt;connection> connection_ptr;
- }
-}
- </pre>
-
- <p>For more information on the <code>odb::connection</code> interface, refer
- to <a href="#3.6">Section 3.6, "Connections"</a>. The first overloaded
- <code>oracle::connection</code> constructor creates a new OCI service
- context. The OCI statement caching is enabled for the underlying session
- while the OCI connection pooling and session pooling are not used. The
- second constructor allows us to create a <code>connection</code> instance by
- providing an already connected Oracle service context. Note that the
- <code>connection</code> instance assumes ownership of this handle. The
- <code>handle()</code> accessor returns the OCI service context handle
- associated with the <code>connection</code> instance.</p>
-
- <p>An OCI error handle is allocated for each <code>connection</code>
- instance and is available via the <code>error_handle()</code> accessor
- function.</p>
-
- <p>Additionally, each <code>connection</code> instance maintains a large
- object (LOB) buffer. This buffer is used by the Oracle ODB runtime
- as an intermediate storage for piecewise handling of LOB data.
- By default, the LOB buffer has zero initial capacity and is
- expanded to 4096 bytes when the first LOB operation is performed.
- If your application requires a bigger or smaller LOB buffer, you can
- specify a custom capacity using the <code>lob_buffer()</code>
- accessor.</p>
-
- <p>The <code>oracle::connection_factory</code> abstract class has the
- following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace oracle
- {
- class connection_factory
- {
- public:
- virtual void
- database (database&amp;) = 0;
-
- virtual connection_ptr
- connect () = 0;
- };
- }
-}
- </pre>
-
- <p>The <code>database()</code> function is called when a connection
- factory is associated with a database instance. This happens in
- the <code>odb::oracle::database</code> class constructors. The
- <code>connect()</code> function is called whenever a database
- connection is requested.</p>
-
- <p>The two implementations of the <code>connection_factory</code>
- interface provided by the Oracle ODB runtime are
- <code>new_connection_factory</code> and
- <code>connection_pool_factory</code>. You will need to include
- the <code>&lt;odb/oracle/connection-factory.hxx></code>
- header file to make the <code>connection_factory</code> interface
- and these implementation classes available in your application.</p>
-
- <p>The <code>new_connection_factory</code> class creates a new
- connection whenever one is requested. When a connection is no
- longer needed, it is released and closed. The
- <code>new_connection_factory</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace oracle
- {
- class new_connection_factory: public connection_factory
- {
- public:
- new_connection_factory ();
- };
-};
- </pre>
-
- <p>The <code>connection_pool_factory</code> class implements a
- connection pool. It has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace oracle
- {
- class connection_pool_factory: public connection_factory
- {
- public:
- connection_pool_factory (std::size_t max_connections = 0,
- std::size_t min_connections = 0);
-
- protected:
- class pooled_connection: public connection
- {
- public:
- pooled_connection (database_type&amp;);
- pooled_connection (database_type&amp;, OCISvcCtx*);
- };
-
- typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
-
- virtual pooled_connection_ptr
- create ();
- };
-};
- </pre>
-
- <p>The <code>max_connections</code> argument in the
- <code>connection_pool_factory</code> constructor specifies the maximum
- number of concurrent connections that this pool factory will
- maintain. Similarly, the <code>min_connections</code> argument
- specifies the minimum number of available connections that
- should be kept open.</p>
-
- <p>Whenever a connection is requested, the pool factory first
- checks if there is an unused connection that can be returned.
- If there is none, the pool factory checks the
- <code>max_connections</code> value to see if a new connection
- can be created. If the total number of connections maintained
- by the pool is less than this value, then a new connection is
- created and returned. Otherwise, the caller is blocked until
- a connection becomes available.</p>
-
- <p>When a connection is released, the pool factory first checks
- if there are blocked callers waiting for a connection. If so, then
- one of them is unblocked and is given the connection. Otherwise,
- the pool factory checks whether the total number of connections
- maintained by the pool is greater than the <code>min_connections</code>
- value. If that's the case, the connection is closed. Otherwise, the
- connection is added to the pool of available connections to be
- returned on the next request. In other words, if the number of
- connections maintained by the pool exceeds <code>min_connections</code>
- and there are no callers waiting for a new connection,
- the pool will close the excess connections.</p>
-
- <p>If the <code>max_connections</code> value is 0, then the pool will
- create a new connection whenever all of the existing connections
- are in use. If the <code>min_connections</code> value is 0, then
- the pool will never close a connection and instead maintain all
- the connections that were ever created.</p>
-
- <p>The <code>create()</code> virtual function is called whenever the
- pool needs to create a new connection. By deriving from the
- <code>connection_pool_factory</code> class and overriding this
- function we can implement custom connection establishment
- and configuration.</p>
-
- <p>If you pass <code>NULL</code> as the connection factory to one of the
- <code>database</code> constructors, then the
- <code>connection_pool_factory</code> instance will be created by default
- with the min and max connections values set to <code>0</code>. The
- following code fragment shows how we can pass our own connection factory
- instance:</p>
-
- <pre class="cxx">
-#include &lt;odb/database.hxx>
-
-#include &lt;odb/oracle/database.hxx>
-#include &lt;odb/oracle/connection-factory.hxx>
-
-int
-main (int argc, char* argv[])
-{
- auto_ptr&lt;odb::oracle::connection_factory> f (
- new odb::oracle::connection_pool_factory (20));
-
- auto_ptr&lt;odb::database> db (
- new oracle::database (argc, argv, false, 0, 0, 0, f));
-}
- </pre>
-
- <h2><a name="20.4">20.4 Oracle Exceptions</a></h2>
-
- <p>The Oracle ODB runtime library defines the following
- Oracle-specific exceptions:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace oracle
- {
- class database_exception: odb::database_exception
- {
- public:
- class record
- {
- public:
- sb4
- error () const;
-
- const std::string&amp;
- message () const;
- };
-
- typedef std::vector&lt;record> records;
-
- typedef records::size_type size_type;
- typedef records::const_iterator iterator;
-
- iterator
- begin () const;
-
- iterator
- end () const;
-
- size_type
- size () const;
-
- virtual const char*
- what () const throw ();
- };
-
- class cli_exception: odb::exception
- {
- public:
- virtual const char*
- what () const throw ();
- };
-
- class invalid_oci_handle: odb::exception
- {
- public:
- virtual const char*
- what () const throw ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/oracle/exceptions.hxx></code>
- header file to make these exceptions available in your application.</p>
-
- <p>The <code>odb::oracle::database_exception</code> is thrown if
- an Oracle database operation fails. The Oracle-specific error
- information is stored as a series of records, each containing
- the error code as a signed 4-byte integer and the message string.
- All this information is also combined and returned in a
- human-readable form by the <code>what()</code> function.</p>
-
- <p>The <code>odb::oracle::cli_exception</code> is thrown by the
- command line parsing constructor of the <code>odb::oracle::database</code>
- class if the Oracle option values are missing or invalid. The
- <code>what()</code> function returns a human-readable description
- of an error.</p>
-
- <p>The <code>odb::oracle::invalid_oci_handle</code> is thrown if an
- invalid handle is passed to an OCI function or if an OCI function
- was unable to allocate a handle. The former normally indicates
- a programming error while the latter indicates an out of memory
- condition. The <code>what()</code> function returns a human-readable
- description of an error.</p>
-
- <h2><a name="20.5">20.5 Oracle Limitations</a></h2>
-
- <p>The following sections describe Oracle-specific limitations imposed
- by the current Oracle and ODB runtime versions.</p>
-
- <h3><a name="20.5.1">20.5.1 Identifier Truncation</a></h3>
-
- <p>Oracle limits the length of database identifiers (table, column, etc.,
- names) to 30 characters. The ODB compiler automatically truncates
- any identifier that is longer than 30 characters. This, however,
- can lead to duplicate names. A common symptom of this problem
- are errors during the database schema creation indicating
- that a database object with the same name already exists. To
- resolve this problem we can assign custom, shorter identifiers
- using the <code>db&nbsp;table</code> and <code>db&nbsp;column</code>
- pragmas (<a href="#14">Chapter 14, "ODB Pragma Language")</a>. For
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class long_class_name
-{
- ...
-
- std::vector&lt;int> long_container_x_;
- std::vector&lt;int> long_container_y_;
-};
- </pre>
-
- <p>In the above example, the names of the two container tables will be
- <code>long_class_name_long_container_x_</code> and
- <code>long_class_name_long_container_y_</code>. However, when
- truncated to 30 characters, they both become
- <code>long_class_name_long_container</code>. To resolve this
- collision we can assign a custom table name for each container:</p>
-
- <pre class="cxx">
-#pragma db object
-class long_class_name
-{
- ...
-
- #pragma db table("long_class_name_cont_x")
- std::vector&lt;int> long_container_x_;
-
- #pragma db table("long_class_name_cont_y")
- std::vector&lt;int> long_container_y_;
-};
- </pre>
-
- <h3><a name="20.5.2">20.5.2 Query Result Caching</a></h3>
-
- <p>Oracle ODB runtime implementation does not perform query result caching
- (<a href="#4.4">Section 4.4, "Query Result"</a>) even when explicitly
- requested. The OCI API supports interleaving execution of multiple
- prepared statements on a single connection. As a result, with OCI,
- it is possible to have multiple uncached results and calls to other
- database functions do not invalidate them. The only limitation of
- the uncached Oracle results is the unavailability of the
- <code>result::size()</code> function. If you call this function on
- an Oracle query result, then the <code>odb::result_not_cached</code>
- exception (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>) is
- always thrown. Future versions of the Oracle ODB runtime library
- may add support for result caching.</p>
-
- <h3><a name="20.5.3">20.5.3 Foreign Key Constraints</a></h3>
-
- <p>ODB assumes the standard SQL behavior which requires that
- foreign key constraints checking is deferred until the
- transaction is committed. Default Oracle behavior is
- to check such constraints immediately. As a result, when
- used with ODB, a custom database schema that defines foreign
- key constraints may need to declare such constraints as
- <code>INITIALLY DEFERRED</code>, as shown in the following example.
- By default, schemas generated by the ODB compiler meet this requirement
- automatically.</p>
-
- <pre class="sql">
-CREATE TABLE Employee (
- ...
- employer NUMBER(20) REFERENCES Employer(id)
- DEFERRABLE INITIALLY DEFERRED);
- </pre>
-
- <p>You can override the default behavior and instruct the ODB
- compiler to generate non-deferrable foreign keys by specifying
- the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
- compiler option. Note, however, that in this case the order in
- which you persist, update, and erase objects within a transaction
- becomes important.</p>
-
- <h3><a name="20.5.4">20.5.4 Unique Constraint Violations</a></h3>
-
- <p>Due to the granularity of the Oracle error codes, it is impossible
- to distinguish between the duplicate primary key and other unique
- constraint violations. As a result, when making an object persistent,
- the Oracle ODB runtime will translate all unique constraint violation
- errors to the <code>object_already_persistent</code> exception
- (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>).</p>
-
- <h3><a name="20.5.5">20.5.5 Large <code>FLOAT</code> and
- <code>NUMBER</code> Types</a></h3>
-
- <p>The Oracle <code>FLOAT</code> type with a binary precision greater
- than 53 and fixed-point <code>NUMBER</code> type with a decimal
- precision greater than 15 cannot be automatically extracted
- into the C++ <code>float</code> and <code>double</code> types.
- Instead, the Oracle ODB runtime uses a 21-byte buffer containing
- the binary representation of a value as an image type for such
- <code>FLOAT</code> and <code>NUMBER</code> types. In order to
- convert them into an application-specific large number representation,
- you will need to provide a suitable <code>value_traits</code>
- template specialization. For more information on the binary format
- used to store the <code>FLOAT</code> and <code>NUMBER</code> values,
- refer to the Oracle Call Interface (OCI) documentation.</p>
-
- <p>An alternative approach to accessing large <code>FLOAT</code> and
- <code>NUMBER</code> values is to map these type to one of the
- natively supported ones, as discussed in <a href="#14.8">Section
- 14.8, "Database Type Mapping Pragmas"</a>.</p>
-
- <p>Note that a <code>NUMBER</code> type that is used to represent a
- floating point number (declared by specifying <code>NUMBER</code>
- without any range and scale) can be extracted into the C++
- <code>float</code> and <code>double</code> types.</p>
-
- <h3><a name="20.5.6">20.5.6 Timezones</a></h3>
-
- <p>ODB does not currently support the Oracle date-time types with timezone
- information. However, these types can be accessed by mapping them to
- one of the natively supported types, as discussed in
- <a href="#14.8">Section 14.8, "Database Type Mapping Pragmas"</a>.</p>
-
- <h3><a name="20.5.7">20.5.7 <code>LONG</code> Types</a></h3>
-
- <p>ODB does not support the deprecated Oracle <code>LONG</code> and
- <code>LONG RAW</code> data types. However, these types can be accessed
- by mapping them to one of the natively supported types, as discussed
- in <a href="#14.8">Section 14.8, "Database Type Mapping Pragmas"</a>.</p>
-
- <h3><a name="20.5.8">20.5.8 LOB Types and By-Value Accessors/Modifiers</a></h3>
-
- <p>As discussed in <a href="#14.4.5">Section 14.4.5,
- "<code>get</code>/<code>set</code>/<code>access</code>"</a>, by-value
- accessor and modifier expressions cannot be used with data members
- of Oracle large object (LOB) data types: <code>BLOB</code>,
- <code>CLOB</code>, and <code>NCLOB</code>. The Oracle ODB runtime
- uses streaming for reading/writing LOB data directly from/to
- data members. As a result, by-reference accessors and modifiers
- should be used for these data types.</p>
-
- <h3><a name="20.5.9">20.5.9 Database Schema Evolution</a></h3>
-
- <p>In Oracle, the type of the <code>name</code> column in the
- <code>schema_version</code> table is <code>VARCHAR2(512)</code>.
- Because this column is a primary key and <code>VARCHAR2</code>
- represents empty strings as <code>NULL</code> values, it is
- impossible to store an empty string in this column, which
- is what is used to represent the default schema name. As a
- result, in Oracle, the empty schema name is stored as a
- string containing a single space character. ODB performs
- all the necessary translations automatically and normally
- you do not need to worry about this implementation detail
- unless you are querying or modifying the <code>schema_version</code>
- table directly.</p>
-
- <h2><a name="20.6">20.6 Oracle Index Definitions</a></h2>
-
- <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
- "Index Definition Pragmas"</a>) is used to define an Oracle index,
- the <code>type</code> clause specifies the index type (for example,
- <code>UNIQUE</code>, <code>BITMAP</code>), the <code>method</code>
- clause is not used, and the <code>options</code> clause specifies
- additional index properties, such as partitioning, table spaces, etc.
- The column options can be used to specify the sort order. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- std::string name_;
-
- #pragma db index \
- type("BITMAP") \
- member(name_, "DESC") \
- options("TABLESPACE TBS1")
-};
- </pre>
-
- <p>Index names in Oracle are schema-global. To avoid name clashes,
- ODB automatically prefixes each index name with the table name on
- which it is defined.</p>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="21">21 Microsoft SQL Server Database</a></h1>
-
- <p>To generate support code for the SQL Server database you will need
- to pass the "<code>--database&nbsp;mssql</code>"
- (or "<code>-d&nbsp;mssql</code>") option to the ODB compiler.
- Your application will also need to link to the SQL Server ODB runtime
- library (<code>libodb-mssql</code>). All SQL Server-specific ODB
- classes are defined in the <code>odb::mssql</code> namespace.</p>
-
- <h2><a name="21.1">21.1 SQL Server Type Mapping</a></h2>
-
- <p>The following table summarizes the default mapping between basic
- C++ value types and SQL Server database types. This mapping can be
- customized on the per-type and per-member basis using the ODB
- Pragma Language (<a href="#14">Chapter 14, "ODB Pragma Language"</a>).</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>C++ Type</th>
- <th>SQL Server Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>bool</code></td>
- <td><code>BIT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char</code></td>
- <td><code>CHAR(1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>signed char</code></td>
- <td><code>TINYINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned char</code></td>
- <td><code>TINYINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>short</code></td>
- <td><code>SMALLINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned short</code></td>
- <td><code>SMALLINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>int</code></td>
- <td><code>INT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned int</code></td>
- <td><code>INT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>float</code></td>
- <td><code>REAL</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>double</code></td>
- <td><code>FLOAT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>std::string</code></td>
- <td><code>VARCHAR(512)/VARCHAR(256)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char[N]</code></td>
- <td><code>VARCHAR(N-1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>std::wstring</code></td>
- <td><code>NVARCHAR(512)/NVARCHAR(256)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>wchar_t[N]</code></td>
- <td><code>NVARCHAR(N-1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>GUID</code></td>
- <td><code>UNIQUEIDENTIFIER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- </table>
-
- <p>It is possible to map the <code>char</code> C++ type to an integer
- database type (for example, <code>TINYINT</code>) using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>).</p>
-
- <p>Note that the <code>std::string</code> and <code>std::wstring</code>
- types are mapped differently depending on whether a member of one of
- these types is an object id or not. If the member is an object id,
- then for this member <code>std::string</code> is mapped
- to <code>VARCHAR(256)</code> and <code>std::wstring</code> &mdash;
- to <code>NVARCHAR(256)</code>. Otherwise, <code>std::string</code>
- is mapped to <code>VARCHAR(512)</code> and <code>std::wstring</code>
- &mdash; to <code>NVARCHAR(512)</code>. Note also that you can
- always change this mapping using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>).</p>
-
- <p>Additionally, by default, C++ enums and C++11 enum classes are
- automatically mapped to the SQL Server types corresponding to their
- underlying integral types (see table above). The default
- <code>NULL</code> semantics is <code>NOT NULL</code>. For
- example:</p>
-
- <pre class="cxx">
-enum color {red, green, blue};
-enum class taste: unsigned char
-{
- bitter = 1,
- sweet,
- sour = 4,
- salty
-};
-
-#pragma db object
-class object
-{
- ...
-
- color color_; // Automatically mapped to INT.
- taste taste_; // Automatically mapped to TINYINT.
-};
- </pre>
-
- <p>Note also that because SQL Server does not support unsigned integers,
- the <code>unsigned&nbsp;short</code>, <code>unsigned&nbsp;int</code>, and
- <code>unsigned&nbsp;long</code>/<code>unsigned&nbsp;long&nbsp;long</code> C++ types
- are by default mapped to the <code>SMALLINT</code>, <code>INT</code>,
- and <code>BIGINT</code> SQL Server types, respectively. The sign bit
- of the value stored by the database for these types will contain
- the most significant bit of the actual unsigned value being
- persisted. Similarly, because there is no signed version of the
- <code>TINYINT</code> SQL Server type, by default, the
- <code>signed char</code> C++ type is mapped to <code>TINYINT</code>.
- As a result, the most significant bit of the value stored by the
- database for this type will contain the sign bit of the actual
- signed value being persisted.</p>
-
- <p>It is also possible to add support for additional SQL Server types,
- such as geospatial types, <code>XML</code>, and user-defined types.
- For more information, refer to <a href="#14.8">Section 14.8, "Database
- Type Mapping Pragmas"</a>.</p>
-
- <h3><a name="21.1.1">21.1.1 String Type Mapping</a></h3>
-
- <p>The SQL Server ODB runtime library provides support for mapping the
- <code>std::string</code>, <code>char[N]</code>, and
- <code>std::array&lt;char, N></code> types to the SQL Server
- <code>CHAR</code>, <code>VARCHAR</code>, and <code>TEXT</code>
- types as well as the <code>std::wstring</code>, <code>wchar_t[N]</code>,
- and <code>std::array&lt;wchar_t, N></code> types to <code>NCHAR</code>,
- <code>NVARCHAR</code>, and <code>NTEXT</code>. However, these mappings
- are not enabled by default (in particular, by default,
- <code>std::array</code> will be treated as a container). To enable the
- alternative mappings for these types we need to specify the database
- type explicitly using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), for
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type ("CHAR(2)")
- char state_[2];
-
- #pragma db type ("NVARCHAR(max)")
- std::wstring text_;
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-#pragma db value(std::wstring) type("NVARCHAR(max)")
-
-#pragma db object
-class object
-{
- ...
-
- std::wstring text_; // Mapped to NVARCHAR(max).
-};
- </pre>
-
- <p>The <code>char[N]</code>, <code>std::array&lt;char, N></code>,
- <code>wchar_t[N]</code>, and <code>std::array&lt;wchar_t, N></code>
- values may or may not be zero-terminated. When extracting such values
- from the database, ODB will append the zero terminator if there is
- enough space.</p>
-
- <p>See also <a href="#21.1.4">Section 21.1.4, "Long String and Binary
- Types"</a> for certain limitations of long string types.</p>
-
- <h3><a name="21.1.2">21.1.2 Binary Type and <code>UNIQUEIDENTIFIER</code> Mapping</a></h3>
-
- <p>The SQL Server ODB runtime library also provides support for mapping the
- <code>std::vector&lt;char></code>,
- <code>std::vector&lt;unsigned&nbsp;char></code>,
- <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
- <code>std::array&lt;char, N></code>, and <code>std::array&lt;unsigned char, N></code>
- types to the SQL Server <code>BINARY</code>, <code>VARBINARY</code>, and
- <code>IMAGE</code> types. There is also support for mapping the
- <code>char[16]</code> array to the SQL Server <code>UNIQUEIDENTIFIER</code>
- type. However, these mappings are not enabled by default (in particular,
- by default, <code>std::vector</code> and <code>std::array</code> will
- be treated as containers). To enable the alternative mappings for these
- types we need to specify the database type explicitly using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("UNIQUEIDENTIFIER")
- char uuid_[16];
-
- #pragma db type("VARBINARY(max)")
- std::vector&lt;char> buf_;
-
- #pragma db type("BINARY(256)")
- unsigned char data_[256];
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;char> buffer;
-#pragma db value(buffer) type("VARBINARY(max)")
-
-#pragma db object
-class object
-{
- ...
-
- buffer buf_; // Mapped to VARBINARY(max).
-};
- </pre>
-
- <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
- the Database"</a>) <code>char[N]</code> and
- <code>std::array&lt;char, N></code> parameters are by default passed
- as a string rather than a binary. To pass such parameters as a binary,
- we need to specify the database type explicitly in the
- <code>_val()</code>/<code>_ref()</code> calls. Note also that we
- don't need to do this for the integrated queries, for example:</p>
-
- <pre class="cxx">
-char u[16] = {...};
-
-db.query&lt;object> ("uuid = " + query::_val&lt;odb::mssql::id_binary> (u));
-db.query&lt;object> (
- "uuid = " + query::_val&lt;odb::mssql::id_uniqueidentifier> (u));
-db.query&lt;object> (query::uuid == query::_ref (u));
- </pre>
-
- <p>See also <a href="#21.1.4">Section 21.1.4, "Long String and Binary
- Types"</a> for certain limitations of long binary types.</p>
-
- <h3><a name="21.1.3">21.1.3 <code>ROWVERSION</code> Mapping</a></h3>
-
- <p><code>ROWVERSION</code> is a special SQL Server data type that is
- automatically incremented by the database server whenever a row
- is inserted or updated. As such, it is normally used to implement
- optimistic concurrency and ODB provides support for using
- <code>ROWVERSION</code> instead of the more portable approach
- for optimistic concurrency (<a href="#12">Chapter 12, "Optimistic
- Concurrency"</a>).</p>
-
- <p><code>ROWVERSION</code> is a 64-bit value which is mapped by ODB
- to <code>unsigned long long</code>. As a result, to use
- <code>ROWVERSION</code> for optimistic concurrency we need to
- make sure that the version column is of the <code>unsigned long
- long</code> type. We also need to explicitly specify that it
- should be mapped to the <code>ROWVERSION</code> data type. For
- example:</p>
-
- <pre class="cxx">
-#pragma db object optimistic
-class person
-{
- ...
-
- #pragma db version type("ROWVERSION")
- unsigned long long version_;
-};
- </pre>
-
- <h3><a name="21.1.4">21.1.4 Long String and Binary Types</a></h3>
-
- <p>For SQL Server, ODB handles character, national character, and
- binary data in two different ways depending on its maximum length.
- If the maximum length (in bytes) is less than or equal to the limit
- specified with the <code>--mssql-short-limit</code> ODB compiler
- option (1024 by default), then it is treated as <i>short data</i>,
- otherwise &mdash; <i>long data</i>. For short data ODB pre-allocates
- an intermediate buffer of the maximum size and binds it directly
- to a parameter or result column. This way the underlying database
- API (ODBC) can read/write directly from/to this buffer. In the case
- of long data, the data is read/written in chunks using the
- <code>SQLGetData()</code>/<code>SQLPutData()</code> ODBC functions.
- While the long data approach reduces the amount of memory used by
- the application, it may require greater CPU resources.</p>
-
- <p>Long data has a number of limitations. In particular, when setting
- a custom short data limit, make sure that it is sufficiently large
- so that no object id in the application is treated as long data.
- It is also impossible to load an object or view with long data more
- than once as part of a query result iteration (<a href="#4.4">Section
- 4.4, "Query Result"</a>). Any such attempt will result in the
- <code>odb::mssql::long_data_reload</code> exception
- (<a href="#21.4">Section 21.4, "SQL Server Exceptions"</a>). For
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- int num_;
-
- #pragma db type("VARCHAR(max)") // Long data.
- std::string str_;
-};
-
-typedef odb::query&lt;object> query;
-typedef odb::result&lt;object> result;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;object> (query::num &lt; 100));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
-{
- if (!i->str_.empty ()) // First load.
- {
- object o;
- i.load (o); // Error: second load, long_data_reload is thrown.
- }
-}
-
-t.commit ();
- </pre>
-
- <p>Finally, if a native view (<a href="#10.6">Section 10.6, "Native
- Views"</a>) contains one or more long data members, then such
- members should come last both in the select-list of the native
- SQL query and the list of data members in the C++ class.</p>
-
- <h2><a name="21.2">21.2 SQL Server Database Class</a></h2>
-
- <p>The SQL Server <code>database</code> class encapsulates the ODBC
- environment handle as well as the server instance address and
- user credentials that are used to establish connections to the
- database. It has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mssql
- {
- enum protocol
- {
- protocol_auto,
- protocol_tcp, // TCP/IP.
- protocol_lpc, // Shared memory (local procedure call).
- protocol_np // Named pipes.
- };
-
- enum transaction_isolation
- {
- isolation_read_uncommitted,
- isolation_read_committed, // SQL Server default.
- isolation_repeatable_read,
- isolation_snapshot,
- isolation_serializable
- };
-
- class database: public odb::database
- {
- public:
- typedef protocol protocol_type;
- typedef transaction_isolation transaction_isolation_type;
-
- database (const std::string&amp; user,
- const std::string&amp; password,
- const std::string&amp; db,
- const std::string&amp; server,
- const std::string&amp; driver = "",
- const std::string&amp; extra_connect_string = "",
- transaction_isolation_type = isolation_read_committed,
- SQLHENV environment = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; user,
- const std::string&amp; password,
- const std::string&amp; db,
- protocol_type protocol = protocol_auto,
- const std::string&amp; host = "",
- const std::string&amp; instance = "",
- const std::string&amp; driver = "",
- const std::string&amp; extra_connect_string = "",
- transaction_isolation_type = isolation_read_committed,
- SQLHENV environment = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; user,
- const std::string&amp; password,
- const std::string&amp; db,
- const std::string&amp; host,
- unsigned int port,
- const std::string&amp; driver = "",
- const std::string&amp; extra_connect_string = "",
- transaction_isolation_type = isolation_read_committed,
- SQLHENV environment = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; connect_string,
- transaction_isolation_type = isolation_read_committed,
- SQLHENV environment = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (int&amp; argc,
- char* argv[],
- bool erase = false,
- const std::string&amp; extra_connect_string = "",
- transaction_isolation_type = isolation_read_committed,
- SQLHENV environment = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- static void
- print_usage (std::ostream&amp;);
-
- public:
- const std::string&amp;
- user () const;
-
- const std::string&amp;
- password () const;
-
- const std::string&amp;
- db () const;
-
- protocol_type
- protocol () const;
-
- const std::string&amp;
- host () const;
-
- const std::string&amp;
- instance () const;
-
- unsigned int
- port () const;
-
- const std::string&amp;
- server () const;
-
- const std::string&amp;
- driver () const;
-
- const std::string&amp;
- extra_connect_string () const;
-
- transaction_isolation_type
- transaction_isolation () const;
-
- const std::string&amp;
- connect_string () const;
-
- SQLHENV
- environment ();
-
- public:
- connection_ptr
- connection ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/mssql/database.hxx></code>
- header file to make this class available in your application.</p>
-
- <p>The overloaded <code>database</code> constructors allow us to specify the
- SQL Server database parameters that should be used when connecting to the
- database. The <code>user</code> and <code>password</code> arguments
- specify the login name and password. If <code>user</code> is empty,
- then Windows authentication is used and the <code>password</code>
- argument is ignored. The <code>db</code> argument specifies the
- database name to open. If it is empty, then the default database for
- the user is used.</p>
-
- <p>The <code>server</code> argument in the first constructor specifies
- the SQL Server instance address in the standard SQL Server address
- format:</p>
-
- <p>
- <code>[<i>protocol</i><b>:</b>]<i>host</i>[<b>\</b><i>instance</i>][<b>,</b><i>port</i>]</code>
- </p>
-
- <p>Where <code><i>protocol</i></code> can be <code>tcp</code>
- (TCP/IP), <code>lpc</code> (shared memory), or
- <code>np</code> (named pipe). If protocol is not specified, then a
- suitable protocol is automatically selected based on the SQL Server
- Native Client configuration. The <code><i>host</i></code> component
- can be a host name or an IP address. If <code><i>instance</i></code>
- is not specified, then the default SQL Server instance is assumed.
- If port is not specified, then the default SQL Server port is
- used (1433). Note that you would normally specify either the
- instance name or the port, but not both. If both are specified,
- then the instance name is ignored by the SQL Server Native Client
- ODBC driver. For more information on the format of the SQL
- Server address, refer to the SQL Server Native Client ODBC
- driver documentation.</p>
-
- <p>The second and third constructors allow us to specify all these address
- components (protocol, host, instance, and port) as separate
- arguments. The third constructor always connects using TCP/IP
- to the specified host and port.</p>
-
- <p>The <code>driver</code> argument specifies the SQL Server Native
- Client ODBC driver that should be used to connect to the database.
- If not specified, then the latest available version is used. The
- following examples show common ways of connecting to the database
- using the first three constructors:</p>
-
- <pre class="cxx">
-// Connect to the default SQL Server instance on the local machine
-// using the default protocol. Login as 'test' with password 'secret'
-// and open the 'example_db' database.
-//
-odb::mssql::database db1 ("test",
- "secret",
- "example_db");
-
-// As above except use Windows authentication and open the default
-// database for this user.
-//
-odb::mssql::database db2 ("",
- "",
- "");
-
-// Connect to the default SQL Server instance on 'onega' using the
-// default protocol. Login as 'test' with password 'secret' and open
-// the 'example_db' database.
-//
-odb::mssql::database db3 ("test",
- "secret",
- "example_db"
- "onega");
-
-// As above but connect to the 'production' SQL Server instance.
-//
-odb::mssql::database db4 ("test",
- "secret",
- "example_db"
- "onega\\production");
-
-// Same as above but specify protocol, host, and instance as separate
-// arguments.
-//
-odb::mssql::database db5 ("test",
- "secret",
- "example_db",
- odb::mssql::protocol_auto,
- "onega",
- "production");
-
-// As above, but use TCP/IP as the protocol.
-//
-odb::mssql::database db6 ("test",
- "secret",
- "example_db"
- "tcp:onega\\production");
-
-// Same as above but using separate arguments.
-//
-odb::mssql::database db7 ("test",
- "secret",
- "example_db",
- odb::mssql::protocol_tcp,
- "onega",
- "production");
-
-// As above, but use TCP/IP port instead of the instance name.
-//
-odb::mssql::database db8 ("test",
- "secret",
- "example_db"
- "tcp:onega,1435");
-
-// Same as above but using separate arguments. Note that here we
-// don't need to specify protocol explicitly since it can only
-// be TCP/IP.
-//
-odb::mssql::database db9 ("test",
- "secret",
- "example_db",
- "onega",
- 1435);
-
-// As above but use the specific SQL Server Native Client ODBC
-// driver version.
-//
-odb::mssql::database dbA ("test",
- "secret",
- "example_db"
- "tcp:onega,1435",
- "SQL Server Native Client 10.0");
- </pre>
-
-
- <p>The fourth constructor allows us to pass a custom ODBC connection
- string that provides all the information necessary to connect to
- the database. Note also that all the other constructors have the
- <code>extra_connect_string</code> argument which can be used to
- specify additional ODBC connection attributes. For more information
- on the format of the ODBC connection string, refer to the SQL
- Server Native Client ODBC driver documentation.</p>
-
- <p>The last constructor extracts the database parameters
- from the command line. The following options are recognized:</p>
-
- <pre class="terminal">
- --user | -U &lt;login>
- --password | -P &lt;password>
- --database | -d &lt;name>
- --server | -S &lt;address>
- --driver &lt;name>
- --options-file &lt;file>
- </pre>
-
- <p>The <code>--options-file</code> option allows us to specify some
- or all of the database options in a file with each option appearing
- on a separate line followed by a space and an option value.</p>
-
- <p>If the <code>erase</code> argument to this constructor is true,
- then the above options are removed from the <code>argv</code>
- array and the <code>argc</code> count is updated accordingly.
- This is primarily useful if your application accepts other
- options or arguments and you would like to get the SQL Server
- options out of the <code>argv</code> array.</p>
-
- <p>This constructor throws the <code>odb::mssql::cli_exception</code>
- exception if the SQL Server option values are missing or invalid. See
- section <a href="#21.4">Section 21.4, "SQL Server Exceptions"</a> for
- more information on this exception.</p>
-
- <p>The static <code>print_usage()</code> function prints the list of options
- with short descriptions that are recognized by this constructor.</p>
-
- <p>Additionally, all the constructors have the <code>transaction_isolation</code>
- and <code>environment</code> arguments. The <code>transaction_isolation</code>
- argument allows us to specify an alternative transaction isolation level
- that should be used by all the connections created by this database instance.
- The <code>environment</code> argument allows us to provide a custom ODBC
- environment handle. If this argument is not <code>NULL</code>, then the
- passed handle is used in all the ODBC function calls made by this
- <code>database</code> instance. Note also that the <code>database</code>
- instance does not assume ownership of the passed environment handle and
- this handle should be valid for the lifetime of the <code>database</code>
- instance.</p>
-
- <p>The last argument to all of the constructors is a pointer to the
- connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
- in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
- non-<code>NULL</code> value, the database instance assumes ownership
- of the factory instance. The connection factory interface as well as
- the available implementations are described in the next section.</p>
-
- <p>The set of accessor functions following the constructors allows us
- to query the parameters of the <code>database</code> instance.</p>
-
- <p>The <code>connection()</code> function returns a pointer to the
- SQL Server database connection encapsulated by the
- <code>odb::mssql::connection</code> class. For more information
- on <code>mssql::connection</code>, refer to <a href="#21.3">Section
- 21.3, "SQL Server Connection and Connection Factory"</a>.</p>
-
- <h2><a name="21.3">21.3 SQL Server Connection and Connection Factory</a></h2>
-
- <p>The <code>mssql::connection</code> class has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mssql
- {
- class connection: public odb::connection
- {
- public:
- connection (database&amp;);
- connection (database&amp;, SQLHDBC handle);
-
- SQLHDBC
- handle ();
-
- details::buffer&amp;
- long_data_buffer ();
- };
-
- typedef details::shared_ptr&lt;connection> connection_ptr;
- }
-}
- </pre>
-
- <p>For more information on the <code>odb::connection</code> interface, refer
- to <a href="#3.6">Section 3.6, "Connections"</a>. The first overloaded
- <code>mssql::connection</code> constructor creates a new ODBC connection.
- The created connection is configured to use the manual commit mode with
- multiple active result sets (MARS) enabled. The second constructor allows
- us to create a <code>connection</code> instance by providing an already
- established ODBC connection. Note that the <code>connection</code>
- instance assumes ownership of this handle. The <code>handle()</code>
- accessor returns the underlying ODBC connection handle associated with
- the <code>connection</code> instance.</p>
-
- <p>Additionally, each <code>connection</code> instance maintains a long
- data buffer. This buffer is used by the SQL Server ODB runtime
- as an intermediate storage for piecewise handling of long data.
- By default, the long data buffer has zero initial capacity and is
- expanded to 4096 bytes when the first long data operation is performed.
- If your application requires a bigger or smaller long data buffer,
- you can specify a custom capacity using the <code>long_data_buffer()</code>
- accessor.</p>
-
- <p>The <code>mssql::connection_factory</code> abstract class has the
- following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mssql
- {
- class connection_factory
- {
- public:
- virtual void
- database (database&amp;) = 0;
-
- virtual connection_ptr
- connect () = 0;
- };
- }
-}
- </pre>
-
- <p>The <code>database()</code> function is called when a connection
- factory is associated with a database instance. This happens in
- the <code>odb::mssql::database</code> class constructors. The
- <code>connect()</code> function is called whenever a database
- connection is requested.</p>
-
- <p>The two implementations of the <code>connection_factory</code>
- interface provided by the SQL Server ODB runtime are
- <code>new_connection_factory</code> and
- <code>connection_pool_factory</code>. You will need to include
- the <code>&lt;odb/mssql/connection-factory.hxx></code>
- header file to make the <code>connection_factory</code> interface
- and these implementation classes available in your application.</p>
-
- <p>The <code>new_connection_factory</code> class creates a new
- connection whenever one is requested. When a connection is no
- longer needed, it is released and closed. The
- <code>new_connection_factory</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mssql
- {
- class new_connection_factory: public connection_factory
- {
- public:
- new_connection_factory ();
- };
-};
- </pre>
-
- <p>The <code>connection_pool_factory</code> class implements a
- connection pool. It has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mssql
- {
- class connection_pool_factory: public connection_factory
- {
- public:
- connection_pool_factory (std::size_t max_connections = 0,
- std::size_t min_connections = 0);
-
- protected:
- class pooled_connection: public connection
- {
- public:
- pooled_connection (database_type&amp;);
- pooled_connection (database_type&amp;, SQLHDBC handle);
- };
-
- typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
-
- virtual pooled_connection_ptr
- create ();
- };
-};
- </pre>
-
- <p>The <code>max_connections</code> argument in the
- <code>connection_pool_factory</code> constructor specifies the maximum
- number of concurrent connections that this pool factory will
- maintain. Similarly, the <code>min_connections</code> argument
- specifies the minimum number of available connections that
- should be kept open.</p>
-
- <p>Whenever a connection is requested, the pool factory first
- checks if there is an unused connection that can be returned.
- If there is none, the pool factory checks the
- <code>max_connections</code> value to see if a new connection
- can be created. If the total number of connections maintained
- by the pool is less than this value, then a new connection is
- created and returned. Otherwise, the caller is blocked until
- a connection becomes available.</p>
-
- <p>When a connection is released, the pool factory first checks
- if there are blocked callers waiting for a connection. If so, then
- one of them is unblocked and is given the connection. Otherwise,
- the pool factory checks whether the total number of connections
- maintained by the pool is greater than the <code>min_connections</code>
- value. If that's the case, the connection is closed. Otherwise, the
- connection is added to the pool of available connections to be
- returned on the next request. In other words, if the number of
- connections maintained by the pool exceeds <code>min_connections</code>
- and there are no callers waiting for a new connection,
- the pool will close the excess connections.</p>
-
- <p>If the <code>max_connections</code> value is 0, then the pool will
- create a new connection whenever all of the existing connections
- are in use. If the <code>min_connections</code> value is 0, then
- the pool will never close a connection and instead maintain all
- the connections that were ever created.</p>
-
- <p>The <code>create()</code> virtual function is called whenever the
- pool needs to create a new connection. By deriving from the
- <code>connection_pool_factory</code> class and overriding this
- function we can implement custom connection establishment
- and configuration.</p>
-
- <p>If you pass <code>NULL</code> as the connection factory to one of the
- <code>database</code> constructors, then the
- <code>connection_pool_factory</code> instance will be created by default
- with the min and max connections values set to <code>0</code>. The
- following code fragment shows how we can pass our own connection factory
- instance:</p>
-
- <pre class="cxx">
-#include &lt;odb/database.hxx>
-
-#include &lt;odb/mssql/database.hxx>
-#include &lt;odb/mssql/connection-factory.hxx>
-
-int
-main (int argc, char* argv[])
-{
- auto_ptr&lt;odb::mssql::connection_factory> f (
- new odb::mssql::connection_pool_factory (20));
-
- auto_ptr&lt;odb::database> db (
- new mssql::database (argc, argv, false, "", 0, f));
-}
- </pre>
-
- <h2><a name="21.4">21.4 SQL Server Exceptions</a></h2>
-
- <p>The SQL Server ODB runtime library defines the following
- SQL Server-specific exceptions:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mssql
- {
- class database_exception: odb::database_exception
- {
- public:
- class record
- {
- public:
- SQLINTEGER
- error () const;
-
- const std::string&amp;
- sqlstate () const;
-
- const std::string&amp;
- message () const;
- };
-
- typedef std::vector&lt;record> records;
-
- typedef records::size_type size_type;
- typedef records::const_iterator iterator;
-
- iterator
- begin () const;
-
- iterator
- end () const;
-
- size_type
- size () const;
-
- virtual const char*
- what () const throw ();
- };
-
- class cli_exception: odb::exception
- {
- public:
- virtual const char*
- what () const throw ();
- };
-
- class long_data_reload: odb::exception
- {
- public:
- virtual const char*
- what () const throw ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/mssql/exceptions.hxx></code>
- header file to make these exceptions available in your application.</p>
-
- <p>The <code>odb::mssql::database_exception</code> is thrown if
- an SQL Server database operation fails. The SQL Server-specific error
- information is stored as a series of records, each containing
- the error code as a signed 4-byte integer, the SQLSTATE code,
- and the message string. All this information is also combined
- and returned in a human-readable form by the <code>what()</code>
- function.</p>
-
- <p>The <code>odb::mssql::cli_exception</code> is thrown by the
- command line parsing constructor of the <code>odb::mssql::database</code>
- class if the SQL Server option values are missing or invalid. The
- <code>what()</code> function returns a human-readable description
- of an error.</p>
-
- <p>The <code>odb::mssql::long_data_reload</code> is thrown if an
- attempt is made to re-load an object or view with long data as
- part of a query result iteration. For more information, refer
- to <a href="#21.1">Section 21.1, "SQL Server Type Mapping"</a>.</p>
-
- <h2><a name="21.5">21.5 SQL Server Limitations</a></h2>
-
- <p>The following sections describe SQL Server-specific limitations imposed
- by the current SQL Server and ODB runtime versions.</p>
-
- <h3><a name="21.5.1">21.5.1 Query Result Caching</a></h3>
-
- <p>SQL Server ODB runtime implementation does not perform query result
- caching (<a href="#4.4">Section 4.4, "Query Result"</a>) even when
- explicitly requested. The ODBC API and the SQL Server Native Client ODBC
- driver support interleaving execution of multiple prepared statements
- on a single connection. As a result, it is possible to have multiple
- uncached results and calls to other database functions do not invalidate
- them. The only limitation of the uncached SQL Server results is the
- unavailability of the <code>result::size()</code> function. If you
- call this function on an SQL Server query result, then the
- <code>odb::result_not_cached</code> exception (<a href="#3.14">Section
- 3.14, "ODB Exceptions"</a>) is always thrown. Future versions of the
- SQL Server ODB runtime library may add support for result caching.</p>
-
- <h3><a name="21.5.2">21.5.2 Foreign Key Constraints</a></h3>
-
- <p>ODB assumes the standard SQL behavior which requires that foreign
- key constraints checking is deferred until the transaction is
- committed. The only behavior supported by SQL Server is to check
- such constraints immediately. As a result, by default, schemas
- generated by the ODB compiler for SQL Server have foreign key
- definitions commented out. They are retained only for documentation.</p>
-
- <p>You can override the default behavior and instruct the ODB
- compiler to generate non-deferrable foreign keys by specifying
- the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
- compiler option. Note, however, that in this case the order in
- which you persist, update, and erase objects within a transaction
- becomes important.</p>
-
- <h3><a name="21.5.3">21.5.3 Unique Constraint Violations</a></h3>
-
- <p>Due to the granularity of the ODBC error codes, it is impossible
- to distinguish between the duplicate primary key and other unique
- constraint violations. As a result, when making an object persistent,
- the SQL Server ODB runtime will translate all unique constraint violation
- errors to the <code>object_already_persistent</code> exception
- (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>).</p>
-
- <h3><a name="21.5.4">21.5.4 Multi-threaded Windows Applications</a></h3>
-
- <p>Multi-threaded Windows applications must use the
- <code>_beginthread()</code>/<code>_beginthreadex()</code> and
- <code>_endthread()</code>/<code>_endthreadex()</code> CRT functions
- instead of the <code>CreateThread()</code> and <code>EndThread()</code>
- Win32 functions to start and terminate threads. This is a limitation of
- the ODBC implementation on Windows.</p>
-
- <h3><a name="21.5.5">21.5.5 Affected Row Count and DDL Statements</a></h3>
-
- <p>SQL Server always returns zero as the number of affected rows
- for DDL statements. In particular, this means that the
- <code>database::execute()</code> (<a href="#3.12">Section 3.12,
- "Executing Native SQL Statements"</a>) function will always
- return zero for such statements.</p>
-
- <h3><a name="21.5.6">21.5.6 Long Data and Auto Object Ids, <code>ROWVERSION</code></a></h3>
-
- <p>SQL Server 2005 has a bug that causes it to fail on an <code>INSERT</code>
- or <code>UPDATE</code> statement with the <code>OUTPUT</code> clause
- (used to return automatically assigned object ids as well as
- <code>ROWVERSION</code> values) if one of the inserted columns
- is long data. The symptom of this bug in ODB is an exception thrown
- by the <code>database::persist()</code> or <code>database::update()</code>
- function when used on an object that contains long data and has an
- automatically assigned object id or uses <code>ROWVERSION</code>-based
- optimistic concurrency (<a href="#21.1.1">Section 21.1.1,
- "<code>ROWVERSION</code> Support"</a>). The error message reads "This
- operation conflicts with another pending operation on this transaction.
- The operation failed."</p>
-
- <p>For automatically assigned object ids ODB includes a workaround for
- this bug which uses a less efficient method to obtain id values for
- objects that contain long data. To enable this workaround you need
- to specify that the generated code will be used with SQL Server 2005
- or later by passing the <code>--mssql-server-version&nbsp;9.0</code>
- ODB compiler option.</p>
-
- <p>For <code>ROWVERSION</code>-based optimistic concurrency no workaround
- is currently provided. The ODB compiler will issue an error for
- objects that use <code>ROWVERSION</code> for optimistic concurrency
- and containing long data.</p>
-
- <h3><a name="21.5.7">21.5.7 Long Data and By-Value Accessors/Modifiers</a></h3>
-
- <p>As discussed in <a href="#14.4.5">Section 14.4.5,
- "<code>get</code>/<code>set</code>/<code>access</code>"</a>, by-value
- accessor and modifier expressions cannot be used with data members
- of long data types. The SQL Server ODB runtime uses streaming for
- reading/writing long data directly from/to data members. As a result,
- by-reference accessors and modifiers should be used for these data
- types.</p>
-
- <h3><a name="21.5.8">21.5.8 Bulk Update and <code>ROWVERSION</code></a></h3>
-
- <p>The bulk update operation (<a href="#15.3">Section 15.3, "Bulk Database
- Operations"</a>) is not yet supported for persistent classes that use
- <code>ROWVERSION</code>-based optimistic concurrency. For such classes
- the bulk <code>update()</code> function is not available. The bulk
- persist and erase support is still provided.</p>
-
- <h2><a name="21.6">21.6 SQL Server Index Definitions</a></h2>
-
- <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
- "Index Definition Pragmas"</a>) is used to define an SQL Server index,
- the <code>type</code> clause specifies the index type (for example,
- <code>UNIQUE</code>, <code>CLUSTERED</code>), the <code>method</code>
- clause is not used, and the <code>options</code> clause specifies
- additional index properties. The column options can be used to specify
- the sort order. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- std::string name_;
-
- #pragma db index \
- type("UNIQUE CLUSTERED") \
- member(name_, "DESC") \
- options("WITH(FILLFACTOR = 80)")
-};
- </pre>
-
- <h2><a name="21.7">21.7 SQL Server Stored Procedures</a></h2>
-
- <p>ODB native views (<a href="#10.6">Section 10.6, "Native Views"</a>)
- can be used to call SQL Server stored procedures. For example, assuming
- we are using the <code>person</code> class from <a href="#2">Chapter
- 2, "Hello World Example"</a> (and the corresponding <code>person</code>
- table), we can create a stored procedure that given the min and max
- ages returns some information about all the people in that range:</p>
-
- <pre class="sql">
-CREATE PROCEDURE dbo.person_range (
- @min_age SMALLINT,
- @max_age SMALLINT)
-AS
- SELECT age, first, last FROM person
- WHERE age >= @min_age AND age &lt;= @max_age;
- </pre>
-
- <p>Given the above stored procedure we can then define an ODB view
- that can be used to call it and retrieve its result:</p>
-
- <pre class="cxx">
-#pragma db view query("EXEC person_range (?)")
-struct person_range
-{
- unsigned short age;
- std::string first;
- std::string last;
-};
- </pre>
-
- <p>The following example shows how we can use the above view to
- print the list of people in a specific age range:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;person_range> query;
-typedef odb::result&lt;person_range> result;
-
-transaction t (db.begin ());
-
-result r (
- db.query&lt;person_range> (
- query::_val (1) + "," + query::_val (18)));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
- cerr &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->age &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>Note that as with all native views, the order and types of data members
- must match those of columns in the <code>SELECT</code> list inside
- the stored procedure.</p>
-
- <p>There are also a number of limitations when it comes to calling
- SQL Server stored procedures with ODB views. There is currently
- no support for output parameters, however, this is planned for
- a future version. In the meantime, to call a stored procedure
- that has output parameters we have to use a wrapper procedure
- that converts such parameters to a <code>SELECT</code>
- result. For example, given the following procedure that
- calculates the age range of the people in our database:</p>
-
- <pre class="sql">
-CREATE PROCEDURE dbo.person_age_range (
- @min_age SMALLINT = NULL OUTPUT,
- @max_age SMALLINT = NULL OUTPUT)
-AS
- SELECT @min_age = MIN(age), @max_age = MAX(max) FROM person;
- </pre>
-
- <p>We can create a wrapper procedure like this:</p>
-
- <pre class="sql">
-CREATE PROCEDURE dbo.person_age_range_odb
-AS
- DECLARE @min_age SMALLINT, @max_age SMALLINT;
- EXEC person_age_range @min_age OUTPUT, @max_age OUTPUT;
- SELECT @min_age, @max_age;
- </pre>
-
- <p>And a view like this:</p>
-
- <pre class="cxx">
-#pragma db view query("EXEC person_age_range_odb")
-struct person_age_range
-{
- unsigned short min_age;
- unsigned short max_age;
-};
- </pre>
-
- <p>Which we can then use to call the stored procedure:</p>
-
- <pre class="cxx">
-transaction t (db.begin ());
-
-person_age_range ar (db.query_value&lt;person_age_range> ());
-cerr &lt;&lt; ar.min_age &lt;&lt; " " &lt;&lt; ar.max_age &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>In SQL Server, a stored procedure can produce multiple results.
- For example, if a stored procedure executes several
- <code>SELECT</code> statements, then the result of calling such
- a procedure consists of two row sets, one for each <code>SELECT</code>
- statement. Because such multiple row sets can contain varying number
- and type of columns, they cannot be all extracted into a
- single view. Consequently, these kind of stored procedures are
- currently not supported.</p>
-
- <p>A stored procedure may also produce no row sets at all. For
- example, a stored procedure that only executes DML statements
- would exhibit this behavior. To call such a procedure we use
- an empty view, for example:</p>
-
- <pre class="sql">
-CREATE PROCEDURE dbo.insert_person (
- @first VARCHAR(512),
- @last VARCHAR(512),
- @age SMALLINT)
-AS
- INSERT INTO person(first, last, age)
- VALUES(@first, @last, @age);
- </pre>
-
- <pre class="cxx">
-#pragma db view
-struct no_result {};
-
-transaction t (db.begin ());
-
-db.query_one&lt;no_result> (
- "EXEC insert_person" +
- query::_val ("John") + "," +
- query::_val ("Doe") + "," +
- query::_val (21));
-
-t.commit ();
- </pre>
-
- <p>Finally, an SQL Server stored procedure can also return an
- integer status code. Similar to output parameters, this code
- can only be observed by an ODB view if it is converted to a
- <code>SELECT</code> result. For more information on how to
- do this and for other examples of stored procedure calls,
- refer to the <code>mssql/stored-proc</code> test in the
- <code>odb-tests</code> package.</p>
-
- <!-- PART -->
-
-
- <hr class="page-break"/>
- <h1><a name="III">PART III&nbsp;&nbsp;
- <span style="font-weight: normal;">PROFILES</span></a></h1>
-
- <p>Part III covers the integration of ODB with popular C++ frameworks
- and libraries. It consists of the following chapters.</p>
-
- <table class="toc">
- <tr><th>22</th><td><a href="#22">Profiles Introduction</a></td></tr>
- <tr><th>23</th><td><a href="#23">Boost Profile</a></td></tr>
- <tr><th>24</th><td><a href="#24">Qt Profile</a></td></tr>
- </table>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="22">22 Profiles Introduction</a></h1>
-
- <p>ODB profiles are a generic mechanism for integrating ODB with
- widely-used C++ frameworks and libraries. A profile provides glue
- code which allows you to seamlessly persist various components, such
- as smart pointers, containers, and value types found in these
- frameworks or libraries. The code necessary to implement a profile
- is packaged into the so called profile library. For example, the
- Boost profile implementation is provided by the <code>libodb-boost</code>
- profile library.</p>
-
- <p>Besides linking the profile library to our application, it is also
- necessary to let the ODB compiler know which profiles we
- are using. This is accomplished with the <code>--profile</code>
- (or <code>-p</code> alias) option. For example:</p>
-
- <pre class="terminal">
-odb --profile boost ...
- </pre>
-
- <p>Some profiles, especially those covering frameworks or libraries that
- consist of multiple sub-libraries, provide sub-profiles that allow you
- to pick and choose which components you would like to use in your
- application. For example, the <code>boost</code> profile contains
- the <code>boost/data-time</code> sub-profile. If we are only
- interested in the <code>date_time</code> types, then we can
- pass <code>boost/data-time</code> instead of <code>boost</code>
- to the <code>--profile</code> option, for example:</p>
-
- <pre class="terminal">
-odb --profile boost/date-time ...
- </pre>
-
- <p>To summarize, you will need to perform the following steps in order
- to make use of a profile in your application:</p>
-
- <ol>
- <li>ODB compiler: if necessary, specify the path to the profile library
- headers (<code>-I</code> option).</li>
- <li>ODB compiler: specify the profile you would like to use with
- the <code>--profile</code> option.</li>
- <li>C++ compiler: if necessary, specify the path to the profile library
- headers (normally <code>-I</code> option).</li>
- <li>Linker: link the profile library to the application.</li>
- </ol>
-
- <p>The remaining chapters in this part of the manual describe the
- standard profiles provided by ODB.</p>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="23">23 Boost Profile</a></h1>
-
- <p>The ODB profile implementation for Boost is provided by the
- <code>libodb-boost</code> library and consists of multiple sub-profiles
- corresponding to the individual Boost libraries. To enable all the
- available Boost sub-profiles, pass <code>boost</code> as the profile
- name to the <code>--profile</code> ODB compiler option. Alternatively,
- you can enable only specific sub-profiles by passing individual
- sub-profile names to <code>--profile</code>. The following sections in
- this chapter discuss each Boost sub-profile in detail. The
- <code>boost</code> example in the <code>odb-examples</code>
- package shows how to enable and use the Boost profile.</p>
-
- <p>Some sub-profiles may throw exceptions to indicate error conditions,
- such as the inability to store a specific value in a particular database
- system. All such exceptions derive from the
- <code>odb::boost::exception</code> class which in turn derives from
- the root of the ODB exception hierarchy, class <code>odb::exception</code>
- (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>). The
- <code>odb::boost::exception</code> class is defined in the
- <code>&lt;odb/boost/exception.hxx></code> header file and has the
- same interface as <code>odb::exception</code>. Concrete exceptions
- that can be thrown by the Boost sub-profiles are described in the
- following sections.</p>
-
- <h2><a name="23.1">23.1 Smart Pointers Library</a></h2>
-
- <p>The <code>smart-ptr</code> sub-profile provides persistence
- support for a subset of smart pointers from the Boost
- <code>smart_ptr</code> library. To enable only this profile,
- pass <code>boost/smart-ptr</code> to the <code>--profile</code>
- ODB compiler option.</p>
-
- <p>The currently supported smart pointers are
- <code>boost::shared_ptr</code> and <code>boost::weak_ptr</code>. For
- more information on using smart pointers as pointers to objects and
- views, refer to <a href="#3.3">Section 3.3, "Object and View Pointers"</a>
- and <a href="#6">Chapter 6, "Relationships"</a>. For more information
- on using smart pointers as pointers to values, refer to
- <a href="#7.3">Section 7.3, "Pointers and <code>NULL</code> Value
- Semantics"</a>. When used as a pointer to a value, only
- <code>boost::shared_ptr</code> is supported. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db null
- boost::shared_ptr&lt;std::string> middle_name_;
-};
- </pre>
-
- <p>To provide finer grained control over object relationship loading,
- the <code>smart-ptr</code> sub-profile also provides the lazy
- counterparts for the above pointers: <code>odb::boost::lazy_shared_ptr</code> and
- <code>odb::boost::lazy_weak_ptr</code>. You will need to include the
- <code>&lt;odb/boost/lazy-ptr.hxx></code> header file to make the lazy
- variants available in your application. For a description of the lazy
- pointer interface and semantics refer to <a href="#6.4">Section 6.4,
- "Lazy Pointers"</a>. The following example shows how we can use these
- smart pointers to establish a relationship between persistent objects.</p>
-
- <pre class="cxx">
-class employee;
-
-#pragma db object
-class position
-{
- ...
-
- #pragma db inverse(position_)
- odb::boost::lazy_weak_ptr&lt;employee> employee_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db not_null
- boost::shared_ptr&lt;position> position_;
-};
- </pre>
-
- <p>Besides providing persistence support for the above smart pointers,
- the <code>smart-ptr</code> sub-profile also changes the default
- pointer (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>)
- to <code>boost::shared_ptr</code>. In particular, this means that
- database functions that return dynamically allocated objects and views
- will return them as <code>boost::shared_ptr</code> pointers. To override
- this behavior, add the <code>--default-pointer</code> option specifying
- the alternative pointer type after the <code>--profile</code> option.</p>
-
- <h2><a name="23.2">23.2 Unordered Containers Library</a></h2>
-
- <p>The <code>unordered</code> sub-profile provides persistence support for
- the containers from the Boost <code>unordered</code> library. To enable
- only this profile, pass <code>boost/unordered</code> to
- the <code>--profile</code> ODB compiler option.</p>
-
- <p>The supported containers are <code>boost::unordered_set</code>,
- <code>boost::unordered_map</code>, <code>boost::unordered_multiset</code>,
- and <code>boost::unordered_multimap</code>. For more information on using
- the set and multiset containers with ODB, refer to <a href="#5.2">Section
- 5.2, "Set and Multiset Containers"</a>. For more information on using the
- map and multimap containers with ODB, refer to <a href="#5.3"> Section
- 5.3, "Map and Multimap Containers"</a>. The following example shows how
- the <code>unordered_set</code> container may be used within a persistent
- object.</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- boost::unordered_set&lt;std::string&gt; emails_;
-};
- </pre>
-
- <h2><a name="23.3">23.3 Multi-Index Container Library</a></h2>
-
- <p>The <code>multi-index</code> sub-profile provides persistence support for
- <code>boost::multi_index_container</code> from the Boost Multi-Index
- library. To enable only this profile, pass <code>boost/multi-index</code>
- to the <code>--profile</code> ODB compiler option. The following example
- shows how <code>multi_index_container</code> may be used within a
- persistent object.</p>
-
- <pre class="cxx">
-namespace mi = boost::multi_index;
-
-#pragma db object
-class person
-{
- ...
-
- typedef
- mi::multi_index_container&lt;
- std::string,
- mi::indexed_by&lt;
- mi::sequenced&lt;>,
- mi::ordered_unique&lt;mi::identity&lt;std::string> >
- >
- > emails;
-
- emails emails_;
-};
- </pre>
-
- <p>Note that a <code>multi_index_container</code> instantiation is
- stored differently in the database depending on whether it has
- any <code>sequenced</code> or <code>random_access</code> indexes.
- If it does, then it is treated as an ordered container
- (<a href="#5.1">Section 5.1, "Ordered Containers"</a>) with the
- first such index establishing the order. Otherwise, it is treated
- as a set container (<a href="#5.2">Section 5.2, "Set and Multiset
- Containers"</a>).</p>
-
- <p>Note also that there is a terminology clash between ODB and Boost
- Multi-Index. The ODB term <em>ordered container</em> translates
- to Multi-Index terms <em>sequenced index</em> and <em>random access
- index</em> while the ODB term <em>set container</em> translates
- to Multi-Index terms <em>ordered index</em> and <em>hashed
- index</em>.</p>
-
- <p>The <code>emails</code> container from the above example is stored
- as an ordered container. In contrast, the following <code>aliases</code>
- container is stored as a set.</p>
-
- <pre class="cxx">
-namespace mi = boost::multi_index;
-
-#pragma db value
-struct name
-{
- std::string first;
- std::string last;
-};
-
-bool operator&lt; (const name&amp;, const name&amp;);
-
-#pragma db object
-class person
-{
- ...
-
- typedef
- mi::multi_index_container&lt;
- name,
- mi::indexed_by&lt;
- mi::ordered_unique&lt;mi::identity&lt;name> >
- mi::ordered_non_unique&lt;
- mi::member&lt;name, std::string, &amp;name::first>
- >,
- mi::ordered_non_unique&lt;
- mi::member&lt;name, std::string, &amp;name::last>
- >
- >
- > aliases;
-
- aliases aliases_;
-};
- </pre>
-
- <h2><a name="23.4">23.4 Optional Library</a></h2>
-
- <p>The <code>optional</code> sub-profile provides persistence support for
- the <code>boost::optional</code> container from the Boost
- <code>optional</code> library. To enable only this profile, pass
- <code>boost/optional</code> to the <code>--profile</code> ODB compiler
- option.</p>
-
- <p>In a relational database <code>boost::optional</code> is mapped to
- a column that can have a <code>NULL</code> value. Similar to
- <code>odb::nullable</code> (<a href="#7.3">Section 7.3, "Pointers and
- <code>NULL</code> Value Semantics"</a>), it can be used to add the
- <code>NULL</code> semantics to existing C++ types. For example:</p>
-
- <pre class="cxx">
-#include &lt;boost/optional.hpp>
-
-#pragma db object
-class person
-{
- ...
-
- std::string first_; // TEXT NOT NULL
- boost::optional&lt;std::string> middle_; // TEXT NULL
- std::string last_; // TEXT NOT NULL
-};
- </pre>
-
- <p>Note also that similar to <code>odb::nullable</code>, when
- this profile is used, the <code>NULL</code> values are automatically
- enabled for data members of the <code>boost::optional</code> type.</p>
-
- <h2><a name="23.5">23.5 Date Time Library</a></h2>
-
- <p>The <code>date-time</code> sub-profile provides persistence support for a
- subset of types from the Boost <code>date_time</code> library. It is
- further subdivided into two sub-profiles, <code>gregorian</code>
- and <code>posix_time</code>. The <code>gregorian</code> sub-profile
- provides support for types from the <code>boost::gregorian</code>
- namespace, while the <code>posix-time</code> sub-profile provides support
- for types from the <code>boost::posix_time</code> namespace. To enable
- the entire <code>date-time</code> sub-profile, pass
- <code>boost/date-time</code> to the <code>--profile</code> ODB compiler
- option. To enable only the <code>gregorian</code> sub-profile, pass
- <code>boost/date-time/gregorian</code>, and to enable only the
- <code>posix-time</code> sub-profile, pass
- <code>boost/date-time/posix-time</code>.</p>
-
- <p>The only type that the <code>gregorian</code> sub-profile currently
- supports is <code>gregorian::date</code>. The types currently supported
- by the <code>posix-time</code> sub-profile are
- <code>posix_time::ptime</code> and
- <code>posix_time::time_duration</code>. The manner in which these types
- are persisted is database system dependent and is discussed in the
- sub-sections that follow. The example below shows how
- <code>gregorian::date</code> may be used within a persistent object.</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- boost::gregorian::date date_of_birth_;
-};
- </pre>
-
- <p>Concrete exceptions that can be thrown by the <code>date-time</code>
- sub-profile implementation are presented below.</p>
-
-
- <pre class="cxx">
-namespace odb
-{
- namespace boost
- {
- namespace date_time
- {
- struct special_value: odb::boost::exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct value_out_of_range: odb::boost::exception
- {
- virtual const char*
- what () const throw ();
- };
- }
- }
-}
- </pre>
-
- <p>You will need to include the
- <code>&lt;odb/boost/date-time/exceptions.hxx&gt;</code> header file to
- make these exceptions available in your application.</p>
-
- <p>The <code>special_value</code> exception is thrown if an attempt is made
- to store a Boost date-time special value that cannot be represented in
- the target database. The <code>value_out_of_range</code> exception is
- thrown if an attempt is made to store a date-time value that is out of
- the target database range. The specific conditions under which these
- exceptions are thrown are database system dependent and are discussed in
- more detail in the following sub-sections.</p>
-
- <h3><a name="23.5.1">23.5.1 MySQL Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Boost <code>date_time</code> types and the MySQL database
- types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost <code>date_time</code> Type</th>
- <th>MySQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>gregorian::date</code></td>
- <td><code>DATE</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::ptime</code></td>
- <td><code>DATETIME</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::time_duration</code></td>
- <td><code>TIME</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
- as a <code>NULL</code> value in a MySQL database.</p>
-
- <p>The <code>posix-time</code> sub-profile implementation also provides
- support for mapping <code>posix_time::ptime</code> to the
- <code>TIMESTAMP</code> MySQL type. However, this mapping has to be
- explicitly requested using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), as shown in
- the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- #pragma db type("TIMESTAMP") not_null
- boost::posix_time::ptime updated_;
-};
- </pre>
-
- <p>Starting with MySQL version 5.6.4 it is possible to store fractional
- seconds up to microsecond precision in <code>TIME</code>,
- <code>DATETIME</code>, and <code>TIMESTAMP</code> columns. However,
- to enable sub-second precision, the corresponding type with the
- desired precision has to be specified explicitly, as shown in the
- following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- #pragma db type("DATETIME(6)") // Microsecond precision.
- boost::posix_time::ptime updated_;
-};
- </pre>
-
- <p>Alternatively, you can enable sub-second precision on the per-type
- basis, for example:</p>
-
- <pre class="cxx">
-#pragma db value(boost::posix_time::ptime) type("DATETIME(6)")
-
-#pragma db object
-class person
-{
- ...
- boost::posix_time::ptime created_; // Microsecond precision.
- boost::posix_time::ptime updated_; // Microsecond precision.
-};
- </pre>
-
- <p>Some valid Boost date-time values cannot be stored in a MySQL database.
- An attempt to persist any Boost date-time special value other than
- <code>date_time::not_a_date_time</code> will result in the
- <code>special_value</code> exception. An attempt to persist a Boost
- date-time value that is out of the MySQL type range will result in
- the <code>out_of_range</code> exception. Refer to the MySQL
- documentation for more information on the MySQL data type ranges.</p>
-
- <h3><a name="23.5.2">23.5.2 SQLite Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Boost <code>date_time</code> types and the SQLite database
- types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost <code>date_time</code> Type</th>
- <th>SQLite Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>gregorian::date</code></td>
- <td><code>TEXT</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::ptime</code></td>
- <td><code>TEXT</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::time_duration</code></td>
- <td><code>TEXT</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
- as a <code>NULL</code> value in an SQLite database.</p>
-
- <p>The <code>date-time</code> sub-profile implementation also provides
- support for mapping <code>gregorian::date</code> and
- <code>posix_time::ptime</code> to the <code>INTEGER</code> SQLite type,
- with the integer value representing the UNIX time. Similarly, an
- alternative mapping for <code>posix_time::time_duration</code> to the
- <code>INTEGER</code> type represents the duration as a number of
- seconds. These mappings have to be explicitly requested using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), as shown in the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- #pragma db type("INTEGER")
- boost::gregorian::date born_;
-};
- </pre>
-
- <!--
-
- <p>The Boost UNIX time interface does not support 64 bit time arithmetic.
- As a result, the UNIX time representations of <code>gregorian::date</code>
- and <code>posix_time::ptime</code> are restricted to the 32 bit range.
- The minimum and maximum date representable by
- <code>gregorian::date</code> is 1901-12-14 and 2038-01-19 respectively,
- while the minimum and maximum date-time representable by
- <code>posix_time::ptime</code> is 1901-12-13&nbsp;20:45:54 GMT and
- 2038-01-19&nbsp;03:14:07&nbsp;GMT respectively. Persisting and loading
- of values outside of these ranges will result in undefined behavior.</p>
-
- -->
-
- <p>Some valid Boost date-time values cannot be stored in an SQLite database.
- An attempt to persist any Boost date-time special value other than
- <code>date_time::not_a_date_time</code> will result in the
- <code>special_value</code> exception. An attempt to persist a negative
- <code>posix_time::time_duration</code> value as SQLite <code>TEXT</code>
- will result in the <code>out_of_range</code> exception.</p>
-
-
- <h3><a name="23.5.3">23.5.3 PostgreSQL Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Boost <code>date_time</code> types and the PostgreSQL database
- types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost <code>date_time</code> Type</th>
- <th>PostgreSQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>gregorian::date</code></td>
- <td><code>DATE</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::ptime</code></td>
- <td><code>TIMESTAMP</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::time_duration</code></td>
- <td><code>TIME</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
- as a <code>NULL</code> value in a PostgreSQL database.
- <code>posix_time::ptime</code> values representing the special values
- <code>date_time::pos_infin</code> and <code>date_time::neg_infin</code>
- are stored as the special PostgreSQL TIMESTAMP values
- <code>infinity</code> and <code>-infinity</code>, respectively.</p>
-
- <p>Some valid Boost date-time values cannot be stored in a PostgreSQL
- database. The PostgreSQL TIME type represents a clock time, and can
- therefore only store positive durations with a total length of time less
- than 24 hours. An attempt to persist a
- <code>posix_time::time_duration</code> value outside of this range will
- result in the <code>value_out_of_range</code> exception. An attempt to
- persist a <code>posix_time::time_duration</code> value representing any
- special value other than <code>date_time::not_a_date_time</code> will
- result in the <code>special_value</code> exception.</p>
-
-
- <h3><a name="23.5.4">23.5.4 Oracle Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Boost <code>date_time</code> types and the Oracle database
- types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost <code>date_time</code> Type</th>
- <th>Oracle Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>gregorian::date</code></td>
- <td><code>DATE</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::ptime</code></td>
- <td><code>TIMESTAMP</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::time_duration</code></td>
- <td><code>INTERVAL DAY TO SECOND</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
- as a <code>NULL</code> value in an Oracle database.</p>
-
- <p>The <code>date-time</code> sub-profile implementation also provides
- support for mapping <code>posix_time::ptime</code> to the
- <code>DATE</code> Oracle type with fractional seconds that may be
- stored in a <code>ptime</code> instance being ignored. This
- alternative mapping has to be explicitly requested using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), as shown in the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- #pragma db type("DATE")
- boost::posix_time::ptime updated_;
-};
- </pre>
-
- <p>Some valid Boost date-time values cannot be stored in an Oracle database.
- An attempt to persist a <code>gregorian::date</code>,
- <code>posix_time::ptime</code>, or
- <code>posix_time::time_duration</code> value representing any special
- value other than <code>date_time::not_a_date_time</code> will result in
- the <code>special_value</code> exception.</p>
-
-
- <h3><a name="23.5.5">23.5.5 SQL Server Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Boost <code>date_time</code> types and the SQL Server database
- types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost <code>date_time</code> Type</th>
- <th>SQL Server Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>gregorian::date</code></td>
- <td><code>DATE</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::ptime</code></td>
- <td><code>DATETIME2</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::time_duration</code></td>
- <td><code>TIME</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
- as a <code>NULL</code> value in an SQL Server database.</p>
-
- <p>Note that the <code>DATE</code>, <code>TIME</code>, and
- <code>DATETIME2</code> types are only available in SQL Server 2008 and
- later. SQL Server 2005 only supports the <code>DATETIME</code> and
- <code>SMALLDATETIME</code> date-time types. The new types are
- also unavailable when connecting to an SQL Server 2008 or
- later with the SQL Server 2005 Native Client ODBC driver.</p>
-
- <p>The <code>date-time</code> sub-profile implementation provides
- support for mapping <code>posix_time::ptime</code> to the
- <code>DATETIME</code> and <code>SMALLDATETIME</code> types,
- however, this mapping has to be explicitly requested using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), as shown in the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- #pragma db type("DATETIME")
- boost::posix_time::ptime updated_;
-};
- </pre>
-
- <p>Some valid Boost date-time values cannot be stored in an SQL Server
- database. An attempt to persist a <code>gregorian::date</code>,
- <code>posix_time::ptime</code>, or <code>posix_time::time_duration</code>
- value representing any special value other than
- <code>date_time::not_a_date_time</code> will result in the
- <code>special_value</code> exception. The range of the <code>TIME</code>
- type in SQL server is from <code>00:00:00.0000000</code> to
- <code>23:59:59.9999999</code>. An attempt to persist a
- <code>posix_time::time_duration</code> value out of this range will
- result in the <code>value_out_of_range</code> exception.</p>
-
- <h2><a name="23.6">23.6 Uuid Library</a></h2>
-
- <p>The <code>uuid</code> sub-profile provides persistence support for the
- <code>uuid</code> type from the Boost <code>uuid</code> library. To
- enable only this profile, pass <code>boost/uuid</code> to the
- <code>--profile</code> ODB compiler option.</p>
-
- <p>The manner in which these types are persisted is database system
- dependent and is discussed in the sub-sections that follow. By
- default a data member of the <code>uuid</code> type is mapped to a
- database column with <code>NULL</code> enabled and nil <code>uuid</code>
- instances are stored as a <code>NULL</code> value. However, you can
- change this behavior by declaring the data member <code>NOT NULL</code>
- with the <code>not_null</code> pragma (<a href="#14.4.6">Section
- 14.4.6, "<code>null</code>/<code>not_null</code>"</a>). In this
- case, or if the data member is an object id, the implementation
- will store nil <code>uuid</code> instances as zero UUID values
- (<code>{00000000-0000-0000-0000-000000000000}</code>). For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- boost::uuids::uuid x_; // Nil values stored as NULL.
-
- #pragma db not_null
- boost::uuids::uuid y_; // Nil values stored as zero.
-};
- </pre>
-
- <h3><a name="23.6.1">23.6.1 MySQL Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the Boost
- <code>uuid</code> type and the MySQL database type.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost Type</th>
- <th>MySQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>boost::uuids::uuid</code></td>
- <td><code>BINARY(16)</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <h3><a name="23.6.2">23.6.2 SQLite Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the Boost
- <code>uuid</code> type and the SQLite database type.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost Type</th>
- <th>SQLite Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>boost::uuids::uuid</code></td>
- <td><code>BLOB</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <h3><a name="23.6.3">23.6.3 PostgreSQL Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the Boost
- <code>uuid</code> type and the PostgreSQL database type.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost Type</th>
- <th>PostgreSQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>boost::uuids::uuid</code></td>
- <td><code>UUID</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <h3><a name="23.6.4">23.6.4 Oracle Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the Boost
- <code>uuid</code> type and the Oracle database type.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost Type</th>
- <th>Oracle Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>boost::uuids::uuid</code></td>
- <td><code>RAW(16)</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <h3><a name="23.6.5">23.6.5 SQL Server Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the Boost
- <code>uuid</code> type and the SQL Server database type.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost Type</th>
- <th>SQL Server Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>boost::uuids::uuid</code></td>
- <td><code>UNIQUEIDENTIFIER</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="24">24 Qt Profile</a></h1>
-
- <p>The ODB profile implementation for Qt is provided by the
- <code>libodb-qt</code> library. Both Qt4 and Qt5 as well
- as C++98/03 and C++11 are supported.</p>
-
- <p>The Qt profile consists of multiple sub-profiles
- corresponding to the common type groups within Qt. Currently,
- only types from the <code>QtCore</code> module are supported. To
- enable all the available Qt sub-profiles, pass <code>qt</code> as the
- profile name to the <code>--profile</code> ODB compiler option.
- Alternatively, you can enable only specific sub-profiles by passing
- individual sub-profile names to <code>--profile</code>. The following
- sections in this chapter discuss each Qt sub-profile in detail. The
- <code>qt</code> example in the <code>odb-examples</code>
- package shows how to enable and use the Qt profile.</p>
-
- <p>Some sub-profiles may throw exceptions to indicate error conditions,
- such as the inability to store a specific value in a particular database
- system. All such exceptions derive from the
- <code>odb::qt::exception</code> class which in turn derives from
- the root of the ODB exception hierarchy, class <code>odb::exception</code>
- (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>). The
- <code>odb::qt::exception</code> class is defined in the
- <code>&lt;odb/qt/exception.hxx></code> header file and has the
- same interface as <code>odb::exception</code>. Concrete exceptions
- that can be thrown by the Qt sub-profiles are described in the
- following sections.</p>
-
- <h2><a name="24.1">24.1 Basic Types Library</a></h2>
-
- <p>The <code>basic</code> sub-profile provides persistence support for basic
- types defined by Qt. To enable only this profile, pass
- <code>qt/basic</code> to the <code>--profile</code> ODB compiler
- option.</p>
-
- <p>The currently supported basic types are <code>QString</code>,
- <code>QByteArray</code>, and <code>QUuid</code>. The manner in
- which these types are persisted is database system dependent
- and is discussed in the sub-sections that follow. The example
- below shows how <code>QString</code> may be used within a
- persistent object.</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
- QString name_;
-};
- </pre>
-
- <p>By default a data member of the <code>QUuid</code> type is mapped to a
- database column with <code>NULL</code> enabled and null <code>QUuid</code>
- instances are stored as a <code>NULL</code> value. However, you can
- change this behavior by declaring the data member <code>NOT NULL</code>
- with the <code>not_null</code> pragma (<a href="#14.4.6">Section
- 14.4.6, "<code>null</code>/<code>not_null</code>"</a>). In this
- case, or if the data member is an object id, the implementation
- will store null <code>QUuid</code> instances as zero UUID values
- (<code>{00000000-0000-0000-0000-000000000000}</code>). For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- QUuid x_; // Null values stored as NULL.
-
- #pragma db not_null
- QUuid y_; // Null values stored as zero.
-};
- </pre>
-
- <h3><a name="24.1.1">24.1.1 MySQL Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported basic Qt types and the MySQL database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Type</th>
- <th>MySQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QString</code></td>
- <td><code>TEXT/VARCHAR(128)</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QByteArray</code></td>
- <td><code>BLOB</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QUuid</code></td>
- <td><code>BINARY(16)</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QString</code> and <code>QByteArray</code>
- types are stored as a <code>NULL</code> value if their
- <code>isNull()</code> member function returns <code>true</code>.</p>
-
- <p>Note also that the <code>QString</code> type is mapped
- differently depending on whether a member of this type
- is an object id or not. If the member is an object id,
- then for this member <code>QString</code> is mapped
- to the <code>VARCHAR(128)</code> MySQL type. Otherwise,
- it is mapped to <code>TEXT</code>.</p>
-
- <p>The <code>basic</code> sub-profile also provides support
- for mapping <code>QString</code> to the <code>CHAR</code>,
- <code>NCHAR</code>, and <code>NVARCHAR</code> MySQL types.
- However, these alternative mappings have to be explicitly
- requested using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "type"</a>), as shown in
- the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
-
- #pragma db type("CHAR(2)") not_null
- QString licenseState_;
-};
- </pre>
-
-
- <h3><a name="24.1.2">24.1.2 SQLite Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported basic Qt types and the SQLite database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Type</th>
- <th>SQLite Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QString</code></td>
- <td><code>TEXT</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QByteArray</code></td>
- <td><code>BLOB</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QUuid</code></td>
- <td><code>BLOB</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QString</code> and <code>QByteArray</code> types
- are stored as a <code>NULL</code> value if their <code>isNull()</code>
- member function returns <code>true</code>.</p>
-
- <h3><a name="24.1.3">24.1.3 PostgreSQL Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported basic Qt types and the PostgreSQL database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Type</th>
- <th>PostgreSQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QString</code></td>
- <td><code>TEXT</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QByteArray</code></td>
- <td><code>BYTEA</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QUuid</code></td>
- <td><code>UUID</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QString</code> and <code>QByteArray</code> types
- are stored as a <code>NULL</code> value if their <code>isNull()</code>
- member function returns <code>true</code>.</p>
-
- <p>The <code>basic</code> sub-profile also provides support
- for mapping <code>QString</code> to the <code>CHAR</code>
- and <code>VARCHAR</code> PostgreSQL types.
- However, these alternative mappings have to be explicitly
- requested using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "type"</a>), as shown in
- the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
-
- #pragma db type("CHAR(2)") not_null
- QString licenseState_;
-};
- </pre>
-
- <h3><a name="24.1.4">24.1.4 Oracle Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported basic Qt types and the Oracle database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Type</th>
- <th>Oracle Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QString</code></td>
- <td><code>VARCHAR2(512)</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QByteArray</code></td>
- <td><code>BLOB</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QUuid</code></td>
- <td><code>RAW(16)</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QString</code> and <code>QByteArray</code> types
- are stored as a <code>NULL</code> value if their <code>isNull()</code>
- member function returns <code>true</code>.</p>
-
- <p>The <code>basic</code> sub-profile also provides support
- for mapping <code>QString</code> to the <code>CHAR</code>,
- <code>NCHAR</code>, <code>NVARCHAR</code>, <code>CLOB</code>, and
- <code>NCLOB</code> Oracle types, and for mapping <code>QByteArray</code>
- to the <code>RAW</code> Oracle type. However, these alternative
- mappings have to be explicitly requested using the <code>db&nbsp;type</code>
- pragma (<a href="#14.4.3">Section 14.4.3, "type"</a>), as shown in the
- following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
-
- #pragma db type("CLOB") not_null
- QString firstName_;
-
- #pragma db type("RAW(16)") null
- QByteArray uuid_;
-};
- </pre>
-
- <h3><a name="24.1.5">24.1.5 SQL Server Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported basic Qt types and the SQL Server database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Type</th>
- <th>SQL Server Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QString</code></td>
- <td><code>VARCHAR(512)/VARCHAR(256)</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QByteArray</code></td>
- <td><code>VARBINARY(max)</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QUuid</code></td>
- <td><code>UNIQUEIDENTIFIER</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QString</code> and <code>QByteArray</code> types
- are stored as a <code>NULL</code> value if their <code>isNull()</code>
- member function returns <code>true</code>.</p>
-
- <p>Note also that the <code>QString</code> type is mapped
- differently depending on whether a member of this type
- is an object id or not. If the member is an object id,
- then for this member <code>QString</code> is mapped
- to the <code>VARCHAR(256)</code> SQL Server type. Otherwise,
- it is mapped to <code>VARCHAR(512)</code>.</p>
-
- <p>The <code>basic</code> sub-profile also provides support
- for mapping <code>QString</code> to the <code>CHAR</code>,
- <code>NCHAR</code>, <code>NVARCHAR</code>, <code>TEXT</code>, and
- <code>NTEXT</code> SQL Server types, and for mapping
- <code>QByteArray</code> to the <code>BINARY</code> and
- <code>IMAGE</code> SQL Server types. However, these alternative
- mappings have to be explicitly requested using the <code>db&nbsp;type</code>
- pragma (<a href="#14.4.3">Section 14.4.3, "type"</a>), as shown in the
- following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
-
- #pragma db type("NVARCHAR(256)") not_null
- QString firstName_;
-
- #pragma db type("BINARY(16)") null
- QByteArray uuid_;
-};
- </pre>
-
- <h2><a name="24.2">24.2 Smart Pointers Library</a></h2>
-
- <p>The <code>smart-ptr</code> sub-profile provides persistence support the
- Qt smart pointers. To enable only this profile, pass
- <code>qt/smart-ptr</code> to the <code>--profile</code> ODB compiler
- option.</p>
-
- <p>The currently supported smart pointers are
- <code>QSharedPointer</code> and <code>QWeakPointer</code>.
- For more information on using smart pointers as pointers to objects
- and views, refer to <a href="#3.3">Section 3.3, "Object and View
- Pointers"</a> and <a href="#6">Chapter 6, "Relationships"</a>. For
- more information on using smart pointers as pointers to values, refer
- to <a href="#7.3">Section 7.3, "Pointers and <code>NULL</code> Value
- Semantics"</a>. When used as a pointer to a value, only
- <code>QSharedPointer</code> is supported. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db null
- QSharedPointer&lt;QString> middle_name_;
-};
- </pre>
-
- <p>To provide finer grained control over object relationship loading,
- the <code>smart-ptr</code> sub-profile also provides the lazy
- counterparts for the above pointers: <code>QLazySharedPointer</code>
- and <code>QLazyWeakPointer</code>. You will need to include the
- <code>&lt;odb/qt/lazy-ptr.hxx></code> header file to make the lazy
- variants available in your application. For a description of the lazy
- pointer interface and semantics refer to <a href="#6.4">Section 6.4,
- "Lazy Pointers"</a>. The following example shows how we can use these
- smart pointers to establish a relationship between persistent objects.</p>
-
- <pre class="cxx">
-class Employee;
-
-#pragma db object
-class Position
-{
- ...
-
- #pragma db inverse(position_)
- QLazyWeakPointer&lt;Employee> employee_;
-};
-
-#pragma db object
-class Employee
-{
- ...
-
- #pragma db not_null
- QSharedPointer&lt;Position> position_;
-};
- </pre>
-
- <p>Besides providing persistence support for the above smart pointers,
- the <code>smart-ptr</code> sub-profile also changes the default
- pointer (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>)
- to <code>QSharedPointer</code>. In particular, this means that
- database functions that return dynamically allocated objects and views
- will return them as <code>QSharedPointer</code> pointers. To override
- this behavior, add the <code>--default-pointer</code> option specifying
- the alternative pointer type after the <code>--profile</code> option.</p>
-
- <h2><a name="24.3">24.3 Containers Library</a></h2>
-
- <p>The <code>containers</code> sub-profile provides persistence support for
- Qt containers. To enable only this profile, pass
- <code>qt/containers</code> to the <code>--profile</code> ODB compiler
- option.</p>
-
- <p>The currently supported ordered containers are <code>QVector</code>,
- <code>QList</code>, and <code>QLinkedList</code>. Supported map
- containers are <code>QMap</code>, <code>QMultiMap</code>,
- <code>QHash</code>, and <code>QMultiHash</code>. The supported set
- container is <code>QSet</code>. For more information on using
- containers with ODB, refer to <a href="#5">Chapter 5, "Containers"</a>.
- The following example shows how the <code>QSet</code> container may
- be used within a persistent object.</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
- QSet&lt;QString> emails_;
-};
- </pre>
-
- <p>The <code>containers</code> sub-profile also provide a change-tracking
- equivalent for <code>QList</code> (<a href="#24.3.1">Section 24.3.1,
- "Change-Tracking <code>QList</code>"</a>) with support for other Qt
- container equivalents planned for future releases. For general information
- on change-tracking containers refer to <a href="#5.4">Section 5.4,
- "Change-Tracking Containers"</a>.</p>
-
- <h3><a name="24.3.1">24.3.1 Change-Tracking <code>QList</code></a></h3>
-
- <p>Class template <code>QOdbList</code>, defined in
- <code>&lt;odb/qt/list.hxx></code>, is a change-tracking
- equivalent for <code>QList</code>. It
- is implemented in terms of <code>QList</code> and is
- implicit-convertible to and implicit-constructible from
- <code>const QList&amp;</code>. In particular, this
- means that we can use <code>QOdbList</code> instance
- anywhere <code>const QList&amp;</code> is
- expected. In addition, <code>QOdbList</code> constant
- iterator (<code>const_iterator</code>) is the same type as
- that of <code>QList</code>.</p>
-
- <p><code>QOdbList</code> incurs 2-bit per element overhead
- in order to store the change state. It cannot
- be stored unordered in the database (<a href="#14.4.19">Section
- 14.4.19 "<code>unordered</code>"</a>) but can be used as an inverse
- side of a relationship (<a href="#6.2">6.2 "Bidirectional
- Relationships"</a>). In this case, no change tracking is performed
- since no state for such a container is stored in the database.</p>
-
- <p>The number of database operations required to update the state
- of <code>QOdbList</code> corresponds well to the complexity
- of <code>QList</code> functions, except for
- <code>prepend()</code>/<code>push_front()</code>. In particular, adding
- or removing an element from the back of the list (for example,
- with <code>append()</code>/<code>push_back()</code> and
- <code>removeLast()</code>/<code>pop_back()</code>),
- requires only a single database statement execution. In contrast,
- inserting or erasing an element at the beginning or in the middle
- of the list will require a database statement for every element that
- follows it.</p>
-
- <p><code>QOdbList</code> replicates most of the <code>QList</code>
- interface as defined in both Qt4 and Qt5 and includes support for
- C++11. However, functions and operators that provide direct write
- access to the elements had to be altered or disabled in order to
- support change tracking. Additional functions used to interface with
- <code>QList</code> and to control the change tracking state
- were also added. The following listing summarizes the differences
- between the <code>QOdbList</code> and <code>QList</code>
- interfaces. Any <code>QList</code> function or operator
- not mentioned in this listing has exactly the same signature
- and semantics in <code>QOdbList</code>. Functions and
- operators that were disabled are shown as commented out and
- are followed by functions/operators that replace them.</p>
-
- <pre class="cxx">
-template &lt;typename T>
-class QOdbList
-{
- ...
-
- // Element access.
- //
-
- //T&amp; operator[] (int);
- T&amp; modify (int);
-
- //T&amp; first();
- T&amp; modifyFirst();
-
- //T&amp; last();
- T&amp; modifyLast();
-
- //T&amp; front();
- T&amp; modify_front();
-
- //T&amp; back();
- T&amp; modify_back();
-
- // Iterators.
- //
- typedef typename QList&lt;T>::const_iterator const_iterator;
-
- class iterator
- {
- ...
-
- // Element Access.
- //
-
- //reference operator* () const;
- const_reference operator* () const;
- reference modify () const;
-
- //pointer operator-> () const;
- const_pointer operator-> () const;
-
- //reference operator[] (difference_type);
- const_reference operator[] (difference_type);
- reference modify (difference_type) const;
-
- // Interfacing with QList::iterator.
- //
- typename QList&lt;T>::iterator base () const;
- };
-
- // Return QList iterators. The begin() functions mark all
- // the elements as modified.
- //
- typename QList&lt;T>::iterator mbegin ();
- typename QList&lt;T>::iterator modifyBegin ();
- typename QList&lt;T>::iterator mend ();
- typename QList&lt;T>::iterator modifyEnd ();
-
- // Interfacing with QList.
- //
- QOdbList (const QList&lt;T>&amp;);
- QOdbList (QList&lt;T>&amp;&amp;); // C++11 only.
-
- QOdbList&amp; operator= (const QList&lt;T>&amp;);
- QOdbList&amp; operator= (QList&lt;T>&amp;&amp;);
-
- operator const QList&lt;T>&amp; () const;
- QList&lt;T>&amp; base ();
- const QList&lt;T>&amp; base () const;
-
- // Change tracking.
- //
- bool _tracking () const;
- void _start () const;
- void _stop () const;
- void _arm (transaction&amp;) const;
-};
- </pre>
-
- <p>The following example highlights some of the differences between
- the two interfaces. <code>QList</code> versions are commented
- out.</p>
-
- <pre class="cxx">
-#include &lt;QtCore/QList>
-#include &lt;odb/qt/list.hxx>
-
-void f (const QList&lt;int>&amp;);
-
-QOdbList&lt;int> l ({1, 2, 3});
-
-f (l); // Ok, implicit conversion.
-
-if (l[1] == 2) // Ok, const access.
- //l[1]++;
- l.modify (1)++;
-
-//l.last () = 4;
-l.modifyLast () = 4;
-
-for (auto i (l.begin ()); i != l.end (); ++i)
-{
- if (*i != 0) // Ok, const access.
- //*i += 10;
- i.modify () += 10;
-}
-
-qSort (l.modifyBegin (), l.modifyEnd ());
- </pre>
-
- <p>Note also the subtle difference between copy/move construction
- and copy/move assignment of <code>QOdbList</code> instances.
- While copy/move constructor will copy/move both the elements as
- well as their change state, in contrast, assignment is tracked
- as any other change to the vector content.</p>
-
- <p>The <code>QListIterator</code> and <code>QMutableListIterator</code>
- equivalents are also provided. These are <code>QOdbListIterator</code>
- and <code>QMutableOdbListIterator</code> and are defined in
- <code>&lt;odb/qt/list-iterator.hxx></code> and
- <code>&lt;odb/qt/mutable-list-iterator.hxx></code>, respectively.</p>
-
- <p><code>QOdbListIterator</code> has exactly the same interface and
- semantics as <code>QListIterator</code>. In fact, we can use
- <code>QListIterator</code> to iterate over a <code>QOdbList</code>
- instance.</p>
-
- <p><code>QMutableOdbListIterator</code> also has exactly the same
- interface as <code>QMutableListIterator</code>. Note, however,
- that any element that such an iterator passes over with the
- call to <code>next()</code> is marked as modified.</p>
-
- <h2><a name="24.4">24.4 Date Time Library</a></h2>
-
- <p>The <code>date-time</code> sub-profile provides persistence support for
- the Qt date-time types. To enable only this profile, pass
- <code>qt/date-time</code> to the <code>--profile</code> ODB compiler
- option.</p>
-
- <p>The currently supported date-time types are <code>QDate</code>,
- <code>QTime</code>, and <code>QDateTime</code>. The manner in which
- these types are persisted is database system dependent and is
- discussed in the sub-sections that follow. The example below shows how
- <code>QDate</code> may be used within a persistent object.</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
- QDate dateOfBirth_;
-};
- </pre>
-
- <p>The single concrete exception that can be thrown by the
- <code>date-time</code> sub-profile implementation is presented below.</p>
-
-
- <pre class="cxx">
-namespace odb
-{
- namespace qt
- {
- namespace date_time
- {
- struct value_out_of_range: odb::qt::exception
- {
- virtual const char*
- what () const throw ();
- };
- }
- }
-}
- </pre>
-
- <p>You will need to include the
- <code>&lt;odb/qt/date-time/exceptions.hxx&gt;</code> header file to
- make this exception available in your application.</p>
-
- <p>The <code>value_out_of_range</code> exception is thrown if an attempt
- is made to store a date-time value that is out of the target database
- range. The specific conditions under which it is thrown is database
- system dependent and is discussed in more detail in the
- following sub-sections.</p>
-
- <h3><a name="24.4.1">24.4.1 MySQL Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Qt date-time types and the MySQL database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Date Time Type</th>
- <th>MySQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QDate</code></td>
- <td><code>DATE</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QTime</code></td>
- <td><code>TIME</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QDateTime</code></td>
- <td><code>DATETIME</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
- <code>QDateTime</code> types are stored as a <code>NULL</code> value
- if their <code>isNull()</code> member function returns true.</p>
-
- <p>The <code>date-time</code> sub-profile implementation also provides
- support for mapping <code>QDateTime</code> to the <code>TIMESTAMP</code>
- MySQL type. However, this mapping has to be explicitly requested using
- the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), as shown in
- the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
- #pragma db type("TIMESTAMP") not_null
- QDateTime updated_;
-};
- </pre>
-
- <p>Starting with MySQL version 5.6.4 it is possible to store fractional
- seconds up to microsecond precision in <code>TIME</code>,
- <code>DATETIME</code>, and <code>TIMESTAMP</code> columns. However,
- to enable sub-second precision, the corresponding type with the
- desired precision has to be specified explicitly, as shown in the
- following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
- #pragma db type("DATETIME(3)") // Millisecond precision.
- QDateTime updated_;
-};
- </pre>
-
- <p>Alternatively, you can enable sub-second precision on the per-type
- basis, for example:</p>
-
- <pre class="cxx">
-#pragma db value(QDateTime) type("DATETIME(3)")
-
-#pragma db object
-class Person
-{
- ...
- QDateTime created_; // Millisecond precision.
- QDateTime updated_; // Millisecond precision.
-};
- </pre>
-
- <p>Some valid Qt date-time values cannot be stored in a MySQL database. An
- attempt to persist a Qt date-time value that is out of the MySQL type
- range will result in the <code>out_of_range</code> exception. Refer to
- the MySQL documentation for more information on the MySQL data type
- ranges.</p>
-
- <h3><a name="24.4.2">24.4.2 SQLite Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Qt date-time types and the SQLite database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Date Time Type</th>
- <th>SQLite Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QDate</code></td>
- <td><code>TEXT</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QTime</code></td>
- <td><code>TEXT</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QDateTime</code></td>
- <td><code>TEXT</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
- <code>QDateTime</code> types are stored as a <code>NULL</code> value
- if their <code>isNull()</code> member function returns true.</p>
-
- <p>The <code>date-time</code> sub-profile implementation also provides
- support for mapping <code>QDate</code> and <code>QDateTime</code> to the
- SQLite <code>INTEGER</code> type, with the integer value representing the
- UNIX time. Similarly, an alternative mapping for <code>QTime</code> to
- the <code>INTEGER</code> type represents a clock time as the number of
- seconds since midnight. These mappings have to be explicitly requested
- using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), as shown
- in the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
- #pragma db type("INTEGER")
- QDate born_;
-};
- </pre>
-
- <p>Some valid Qt date-time values cannot be stored in an SQLite database.
- An attempt to persist any Qt date-time value representing a negative UNIX
- time (any point in time prior to the 1970-01-01&nbsp;00:00:00 UNIX time
- epoch) as an SQLite <code>INTEGER</code> will result in the
- <code>out_of_range</code> exception.</p>
-
- <h3><a name="24.4.3">24.4.3 PostgreSQL Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Qt date-time types and the PostgreSQL database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Date Time Type</th>
- <th>PostgreSQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QDate</code></td>
- <td><code>DATE</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QTime</code></td>
- <td><code>TIME</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QDateTime</code></td>
- <td><code>TIMESTAMP</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
- <code>QDateTime</code> types are stored as a <code>NULL</code> value
- if their <code>isNull()</code> member function returns true.</p>
-
- <h3><a name="24.4.4">24.4.4 Oracle Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Qt date-time types and the Oracle database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Date Time Type</th>
- <th>Oracle Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QDate</code></td>
- <td><code>DATE</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QTime</code></td>
- <td><code>INTERVAL DAY(0) TO SECOND(3)</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QDateTime</code></td>
- <td><code>TIMESTAMP(3)</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
- <code>QDateTime</code> types are stored as a <code>NULL</code> value
- if their <code>isNull()</code> member function returns true.</p>
-
- <p>The <code>date-time</code> sub-profile implementation also provides
- support for mapping <code>QDateTime</code> to the
- <code>DATE</code> Oracle type with fractional seconds that may be
- stored in a <code>QDateTime</code> instance being ignored. This
- alternative mapping has to be explicitly requested using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), as shown in the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- #pragma db type("DATE")
- QDateTime updated_;
-};
- </pre>
-
- <h3><a name="24.4.5">24.4.5 SQL Server Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Qt date-time types and the SQL Server database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Date Time Type</th>
- <th>SQL Server Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QDate</code></td>
- <td><code>DATE</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QTime</code></td>
- <td><code>TIME(3)</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QDateTime</code></td>
- <td><code>DATETIME2(3)</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
- <code>QDateTime</code> types are stored as a <code>NULL</code> value
- if their <code>isNull()</code> member function returns true.</p>
-
- <p>Note that the <code>DATE</code>, <code>TIME</code>, and
- <code>DATETIME2</code> types are only available in SQL Server 2008 and
- later. SQL Server 2005 only supports the <code>DATETIME</code> and
- <code>SMALLDATETIME</code> date-time types. The new types are
- also unavailable when connecting to an SQL Server 2008 or
- later with the SQL Server 2005 Native Client ODBC driver.</p>
-
- <p>The <code>date-time</code> sub-profile implementation provides
- support for mapping <code>QDateTime</code> to the <code>DATETIME</code>
- and <code>SMALLDATETIME</code> types, however, this mapping has to
- be explicitly requested using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), as
- shown in the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- #pragma db type("DATETIME")
- QDateTime updated_;
-};
- </pre>
-
- </div>
-</div>
-
-</body>
-</html>