diff options
Diffstat (limited to 'doc')
-rw-r--r-- | doc/.gitignore | 5 | ||||
-rw-r--r-- | doc/Makefile.am | 7 | ||||
-rw-r--r-- | doc/buildfile | 191 | ||||
-rw-r--r-- | doc/default.css | 323 | ||||
-rw-r--r-- | doc/makefile | 80 | ||||
-rw-r--r-- | doc/manual.html2ps | 69 | ||||
-rw-r--r-- | doc/manual.xhtml | 27073 | ||||
-rw-r--r-- | doc/odb-arch.png | bin | 16883 -> 0 bytes | |||
-rw-r--r-- | doc/odb-arch.svg | 410 | ||||
-rw-r--r-- | doc/odb-epilogue.1 | 153 | ||||
-rw-r--r-- | doc/odb-epilogue.xhtml | 126 | ||||
-rw-r--r-- | doc/odb-flow.png | bin | 39092 -> 0 bytes | |||
-rw-r--r-- | doc/odb-flow.svg | 822 | ||||
-rw-r--r-- | doc/odb-prologue.1 | 84 | ||||
-rw-r--r-- | doc/odb-prologue.xhtml | 88 | ||||
-rw-r--r-- | doc/pregenerated/odb.1 | 799 | ||||
-rw-r--r-- | doc/pregenerated/odb.xhtml | 978 |
17 files changed, 0 insertions, 31208 deletions
diff --git a/doc/.gitignore b/doc/.gitignore deleted file mode 100644 index 9ee2af2..0000000 --- a/doc/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/odb.1 -/odb.xhtml - -*.ps -*.pdf diff --git a/doc/Makefile.am b/doc/Makefile.am deleted file mode 100644 index f09e34b..0000000 --- a/doc/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -# file : doc/Makefile.am -# license : GNU GPL v3; see accompanying LICENSE file - -dist_ps_DATA = __file__(ps_docs) -dist_pdf_DATA = __file__(pdf_docs) -dist_html_DATA = __file__(html_docs) -dist_man_MANS = __file__(man_docs) diff --git a/doc/buildfile b/doc/buildfile deleted file mode 100644 index 88f30fc..0000000 --- a/doc/buildfile +++ /dev/null @@ -1,191 +0,0 @@ -# file : doc/buildfile -# license : GNU GPL v3; see accompanying LICENSE file - -define css: doc -css{*}: extension = css - -define xhtml: doc -xhtml{*}: extension = xhtml - -define ps: doc -ps{*}: extension = ps - -define pdf: doc -pdf{*}: extension = pdf - -define html2ps: file -html2ps{*}: extension = html2ps - -./: css{default} xhtml{manual} doc{*.png} file{*.svg} - -# Man pages. -# - -## Consumption build ($develop == false). -# - -# Use pregenerated versions in the consumption build. -# -./: pregenerated/{man1 xhtml}{*}: include = (!$develop) - -# Distribute pregenerated versions only in the consumption build. -# -pregenerated/{man1 xhtml}{*}: dist = (!$develop) - -# -## - -## Development build ($develop == true). -# - -./: {man1 xhtml}{odb}: include = $develop - -if $develop -{ - doc_version = [string] "$version.major.$version.minor.$version.patch" - if $version.pre_release - doc_version += "-$version.pre_release_string" - - # Let's take the last four-digit number to cover 2000-2021,2022. - # - doc_year = $regex.replace($copyright, '.+[-, ]([0-9][0-9][0-9][0-9]) .+', '\1') - - man_options = -v project="ODB" -v version="$doc_version" \ - -v copyright="$copyright" --suppress-undocumented - - import! [metadata] cli = cli%exe{cli} -} - -# In the development build distribute regenerated versions, remapping their -# locations to the paths of the pregenerated versions (which are only -# distributed in the consumption build; see above). This way we make sure that -# the distributed files are always up-to-date. -# -{man1 xhtml}{odb}: dist = ($develop ? pregenerated/ : false) - -# @@ TMP Note that the project, version, and date variables we are passing to -# cli are currently unused since the respective values are hard-coded -# in the odb-prologue.* files. -# -man1{odb}: ../odb/cli{options} file{odb-prologue.1 odb-epilogue.1} $cli -% -if $develop -{{ - # Use the copyright year to approximate the last authoring date. - # - $cli --generate-man $man_options \ - -v date="January $doc_year" \ - --man-prologue-file $path($<[1]) \ - --man-epilogue-file $path($<[2]) \ - --stdout $path($<[0]) >$path($>) - - # If the result differs from the pregenerated version, copy it over. - # - if! diff $src_base/pregenerated/odb.1 $path($>) >- - cp $path($>) $src_base/pregenerated/odb.1 - end -}} - -xhtml{odb}: ../odb/cli{options} file{odb-prologue.xhtml odb-epilogue.xhtml} $cli -% -if $develop -{{ - $cli --generate-html $man_options \ - --html-prologue-file $path($<[1]) \ - --html-epilogue-file $path($<[2]) \ - --stdout $path($<[0]) >$path($>) - - if! diff $src_base/pregenerated/odb.xhtml $path($>) >- - cp $path($>) $src_base/pregenerated/odb.xhtml - end -}} - -# -## - -# Manual. -# -# This case is slightly more involved because we make the generation of the -# manual's ps/pdf optional and also don't keep the result in the repository. -# Specifically: -# -# 1. In the consumption build we will install/redistribute ps/pdf if present. -# -# 2. In the development build we will generate ps/pdf if we are able to import -# the needed tools, issuing a warning otherwise. - -## Consumption build ($develop == false). -# - -# Use pregenerated versions, if exist, in the consumption build. -# -./: pregenerated/{ps pdf}{*}: include = (!$develop) - -# Distribute pregenerated versions only in the consumption build. -# -pregenerated/{ps pdf}{*}: dist = (!$develop) - -# -## - -## Development build ($develop == true). -# - -html2pdf = false - -if $develop -{ - # Import the html2ps and ps2pdf programs from the system, if available. - # - import? html2ps = html2ps%exe{html2ps} - import? ps2pdf = ps2pdf14%exe{ps2pdf14} - - html2pdf = ($html2ps != [null] && $ps2pdf != [null]) - - if! $html2pdf - warn "html2ps and/or ps2pdf14 are not available, not generating .ps and .pdf documentation" -} - -./: {ps pdf}{odb-manual}: include = $html2pdf - -# In the development build distribute regenerated versions, remapping their -# locations to the paths of the pregenerated versions (which are only -# distributed in the consumption build; see above). This way we make sure that -# the distributed files are always up-to-date. -# -{ps pdf}{odb-manual}: dist = ($html2pdf ? pregenerated/ : false) - -# Note: the pregenerated file may not exist, thus --no-cleanup option is -# required for the cp builtin call. Strictly speaking we don't really need to -# copy them since they are not stored in the repository, but let's do that for -# consistency with the distributed source tree. -# -# @@ TMP Note that manual.{xhtml,html2ps} still have copyright years, ODB -# version, document revision/date, etc hard-coded. -# -ps{odb-manual}: {xhtml html2ps}{manual} $html2ps -% -if $html2pdf -{{ - options = - - diag html2ps ($<[0]) - $html2ps $options -f $path($<[1]) -o $path($>) $path($<[0]) - - cp --no-cleanup $path($>) $src_base/pregenerated/odb-manual.ps -}} - -pdf{odb-manual}: ps{odb-manual} $ps2pdf -% -if $html2pdf -{{ - options = -dOptimize=true -dEmbedAllFonts=true - - diag ps2pdf ($<[0]) - $ps2pdf $options $path($<[0]) $path($>) - - cp --no-cleanup $path($>) $src_base/pregenerated/odb-manual.pdf -}} - -# -## diff --git a/doc/default.css b/doc/default.css deleted file mode 100644 index 889f46b..0000000 --- a/doc/default.css +++ /dev/null @@ -1,323 +0,0 @@ -html { - margin : 0; - padding : 0; - background : white; -} - -body { - font-family : "Lucida Grande", Verdana, "Bitstream Vera Sans", sans-serif; - font-weight : normal; - font-size : 13px; - line-height : 19px; - - color : black; - - margin : 0 2em 0 2em; - padding : 0; -} - - -body { - min-width: 40em; -} - -#container { - max-width : 46em; - margin : 0 auto; - padding : 0 1em 0 1em; -} - - - -/* - * Footer - * - */ -#footer { - color : #3a84a7; - - padding : 1em 0 0.5em 0; - - font-size : 10px; - line-height : 15px; - - text-align: center; -} - -#footer a:link, #footer a:visited { - - color:#1d6699; - text-decoration: underline; -} - -#footer a { - margin-left: 0.7em; - margin-right: 0.7em; -} - -#footer p { - padding: 0; - margin: 0.3em 0 0 0; -} - -/* Distribution terms. */ -#footer #terms { - text-align: justify; - - font-size : 110%; - font-family : monospace; - - padding : 1em 0 0.5em 0; -} - - -/* - * Content - * - */ - -#content { - padding : 0em 0.1em 0 1.3em; - margin : 1.4em 0 0 0; -} - -#content p, -#content ol, -#content ul, -#content dl { - text-align: justify; -} - -#content h1 { - margin-left: -0.89em; -} - -a:link { - color:#0536d2; -} - - -/* - * Headings - * - */ - -h1, h2, h3, h4, h5, h6 { - font-weight : 500; -} - -h1 { font-size : 155%; } -h2 { font-size : 130%; } -h3 { font-size : 125%; } -h4 { font-size : 110%; } -h5 { font-size : 106%; } -h6 { font-size : 100%; } - -h1 { margin : 1.8em 0 0.8em 0;} -h2 { margin-top : 1.4em;} -h3 { margin-top : 1em;} - -p.indent { - margin-left : 1.5em; -} - - -/* - * Fix for IE 5.5 table font problem - * - */ - -table { - font-size : 13px; -} - - -/* - * table of content - * - */ - -ul.toc li { - padding : .4em 0em 0em 0em; -} - - -/* Toc links don't need to show when they are visited. */ -.toc a:visited { - color:#0536d2; -} - - -/* - * lists - * - */ - - -/* list of links */ -ul.menu { - list-style-type : none; -} - -ul.menu li { - padding-top : 0.3em; - padding-bottom : 0.3em; -} - - - -/* @@ I should probably use child selector here */ -/* list with multiline list-elements */ -ul.multiline li, ol.multiline li, dl.multiline dd { - padding-top : 0.16em; - padding-bottom : 0.16em; - - font-size : 11px; - line-height : 15px; -} - - - -/* C++ code snippet */ -pre.cxx { - margin-top : 0em; - margin-bottom : 2em; - - margin-left : 1em; -} - -/* SQL code snippet */ -pre.sql { - margin-top : 0em; - margin-bottom : 2em; - - margin-left : 1em; -} - -/* make code snippet */ -pre.make { - margin-top : 0em; - margin-bottom : 2em; - - margin-left : 1em; -} - -/* terminal output */ -pre.term { - margin-top : 0em; - margin-bottom : 2em; - - margin-left : 1em; -} - - -/* Images */ -div.center { - text-align: center; -} - -/* Document info. */ -#docinfo { - margin-top: 4em; - border-top: 1px dashed #000000; - font-size: 70%; -} - - -/* Footnote */ - -#footnote { - margin-top : 2.5em; -} - -#footnote hr, hr.footnote { - margin-left: 0; - margin-bottom: 0.6em; - width: 8em; - border-top: 1px solid #000000; - border-right: none; - border-bottom: none; - border-left: none; - -} - -#footnote ol { - margin-left: 0; - padding-left: 1.45em; -} - -#footnote li { - text-align : left; - font-size : 11px; - line-height : 15px; - - padding : .4em 0 .4em 0; -} - - -/* Normal table with borders, etc. */ - -table.std { - margin: 2em 0 2em 0; - - border-collapse : collapse; - border : 1px solid; - border-color : #000000; - - font-size : 11px; - line-height : 14px; -} - -table.std th, table.std td { - border : 1px solid; - padding : 0.6em 0.8em 0.6em 0.8em; -} - -table.std th { - background : #cde8f6; -} - -table.std td { - text-align: left; -} - - -/* - * "item | description" table. - * - */ - -table.description { - border-style : none; - border-collapse : separate; - border-spacing : 0; - - font-size : 13px; - - margin : 0.6em 0 0.6em 0; - padding : 0 0 0 0; -} - -table.description tr { - padding : 0 0 0 0; - margin : 0 0 0 0; -} - -table.description * td, table.description * th { - border-style : none; - margin : 0 0 0 0; - vertical-align : top; -} - -table.description * th { - font-weight : normal; - padding : 0.4em 1em 0.4em 0; - text-align : left; - white-space : nowrap; - background : none; -} - -table.description * td { - padding : 0.4em 0 0.4em 1em; - text-align : justify; -} diff --git a/doc/makefile b/doc/makefile deleted file mode 100644 index 481f22d..0000000 --- a/doc/makefile +++ /dev/null @@ -1,80 +0,0 @@ -# file : doc/makefile -# license : GNU GPL v3; see accompanying LICENSE file - -include $(dir $(lastword $(MAKEFILE_LIST)))../build/bootstrap.make - -default := $(out_base)/ -dist := $(out_base)/.dist -clean := $(out_base)/.clean - -# Import. -# -$(call import,\ - $(scf_root)/import/cli/stub.make,\ - cli: cli,cli-rules: cli_rules) - -# Build. -# -$(default): \ -$(out_base)/odb.1 \ -$(out_base)/odb.xhtml \ -$(out_base)/odb-manual.ps \ -$(out_base)/odb-manual.pdf - -# Man/html pages. -# -$(out_base)/odb.xhtml $(out_base)/odb.1: cli := $(cli) - -$(out_base)/odb.xhtml: $(src_root)/odb/options.cli \ - $(src_base)/odb-prologue.xhtml \ - $(src_base)/odb-epilogue.xhtml | $(out_base)/. - $(call message,cli-html $<,$(cli) --generate-html --stdout \ ---suppress-undocumented \ ---html-prologue-file $(src_base)/odb-prologue.xhtml \ ---html-epilogue-file $(src_base)/odb-epilogue.xhtml $< >$@) - -$(out_base)/odb.1: $(src_root)/odb/options.cli \ - $(src_base)/odb-prologue.1 \ - $(src_base)/odb-epilogue.1 | $(out_base)/. - $(call message,cli-man $<,$(cli) --generate-man --stdout \ ---suppress-undocumented \ ---man-prologue-file $(src_base)/odb-prologue.1 \ ---man-epilogue-file $(src_base)/odb-epilogue.1 $< >$@) - -# Manual. -# -$(out_base)/odb-manual.ps: $(src_base)/manual.xhtml \ - $(src_base)/manual.html2ps \ - $(src_base)/odb-arch.png \ - $(src_base)/odb-flow.png | $(out_base)/. - $(call message,html2ps $<,html2ps -f $(src_base)/manual.html2ps -o $@ $<) - -$(out_base)/odb-manual.pdf: $(out_base)/odb-manual.ps - $(call message,ps2pdf $<,ps2pdf14 $< $@) - -# Dist. -# -$(dist): data_dist := default.css manual.xhtml odb-arch.png odb-flow.png -$(dist): export ps_docs := odb-manual.ps -$(dist): export pdf_docs := odb-manual.pdf -$(dist): export html_docs := $(data_dist) odb.xhtml -$(dist): export man_docs := odb.1 -$(dist): \ -$(out_base)/odb.1 \ -$(out_base)/odb.xhtml \ -$(out_base)/odb-manual.ps \ -$(out_base)/odb-manual.pdf - $(call dist-data,$^) - $(call dist-data,$(data_dist)) - $(call meta-automake) - -# Clean. -# -$(clean): - $(call message,rm $$1,rm -f $$1,$(out_base)/odb.1) - $(call message,rm $$1,rm -f $$1,$(out_base)/odb.xhtml) - $(call message,rm $$1,rm -f $$1,$(out_base)/odb-manual.ps) - $(call message,rm $$1,rm -f $$1,$(out_base)/odb-manual.pdf) - -$(call include,$(bld_root)/dist.make) -$(call include,$(bld_root)/meta/automake.make) diff --git a/doc/manual.html2ps b/doc/manual.html2ps deleted file mode 100644 index 6acd29d..0000000 --- a/doc/manual.html2ps +++ /dev/null @@ -1,69 +0,0 @@ -@html2ps { - option { - toc: hb; - colour: 1; - hyphenate: 1; - titlepage: 1; - } - - datefmt: "%B %Y"; - - titlepage { - content: " -<div align=center> - <h1><big>C++ Object Persistence with ODB</big></h1> - <h1> </h1> - <h1> </h1> - <h1> </h1> - <h1> </h1> - <h1> </h1> - <h1> </h1> - <h1> </h1> - <h1> </h1> -</div> - <p>Copyright © 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> - - <p>Revision $[revision], $D</p> - - <p>This revision of the manual describes ODB $[version] 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>"; - } - - toc { - indent: 2em; - } - - header { - odd-right: $H; - even-left: $H; - } - - footer { - odd-left: Revision $[revision], $D; - odd-center: $T; - odd-right: $N; - - even-left: $N; - even-center: $T; - even-right: Revision $[revision], $D; - } -} - -body { - font-size: 12pt; - text-align: justify; -} - -pre { - font-size: 10pt; -} diff --git a/doc/manual.xhtml b/doc/manual.xhtml deleted file mode 100644 index a308758..0000000 --- a/doc/manual.xhtml +++ /dev/null @@ -1,27073 +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="© 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 © 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> - <tr><th>14.1.16</th><td><a href="#14.1.16"><code>options</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 - <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-<database></code>, where <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 <string> - -class person -{ -public: - person (const std::string& first, - const std::string& last, - unsigned short age); - - const std::string& first () const; - const std::string& 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 <string> - -#include <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><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 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 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& email () const; - void email (const std::string&); - - const std::string& get_name () const; - std::string& 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 < 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 <memory> // std::auto_ptr -#include <iostream> - -#include <odb/database.hxx> -#include <odb/transaction.hxx> - -#include <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<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& e) - { - cerr << e.what () << 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><odb/database.hxx></code> - and <code><odb/transaction.hxx></code> which define database - system-independent <code>odb::database</code> and - <code>odb::transaction</code> interfaces. Then we include - <code><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 < 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<person> query; - typedef odb::result<person> result; - - // Say hello to those over 30. - // - { - transaction t (db->begin ()); - - result r (db->query<person> (query::age > 30)); - - for (result::iterator i (r.begin ()); i != r.end (); ++i) - { - cout << "Hello, " << i->first () << "!" << endl; - } - - t.commit (); - } - } - catch (const odb::exception& e) - { - cerr << e.what () << 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 < 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 << "Hello, " << i->first () << " (" << i->id () << ")!" << 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<person> joe (db->load<person> (joe_id)); - joe->age (joe->age () + 1); - db->update (*joe); - - t.commit (); - } - - // Say hello to those over 30. - // - { - ... - } - } - catch (const odb::exception& e) - { - cerr << e.what () << 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 < 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<person> joe ( - db->query_one<person> (query::first == "Joe" && - 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<person_stat> ()); - - cout << "count : " << ps.count << endl - << "min age: " << ps.min_age << endl - << "max age: " << ps.max_age << 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<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<person> john ( - db->query_one<person> (query::first == "John" && - 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 < 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 < 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::<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<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::<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 <string> -#include <memory> // std::auto_ptr -#include <iostream> - -#include <odb/database.hxx> -#include <odb/transaction.hxx> - -#include <odb/mysql/database.hxx> -#include <odb/pgsql/database.hxx> - -#include "person.hxx" -#include "person-odb.hxx" - -using namespace std; -using namespace odb::core; - -auto_ptr<database> -create_database (int argc, char* argv[]) -{ - auto_ptr<database> r; - - if (argc < 2) - { - cerr << "error: database system name expected" << 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 << "error: unknown database system " << db << endl; - - return r; -} - -int -main (int argc, char* argv[]) -{ - try - { - auto_ptr<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 < 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 — 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 — 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 — 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 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 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><odb/core.hxx></code> header, a - friend of this object class. For example:</p> - - <pre class="cxx"> -#include <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 <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& get_name () const; - std::string& 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 get</code> - and <code>db 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 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 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 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 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 <odb/database.hxx> -#include <odb/mysql/database.hxx> - -auto_ptr<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><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 <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><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&, - const std::string& name = "", - bool drop = true); - - static void - drop_schema (database&, const std::string& name = ""); - - static bool - exists (database_id, const std::string& name = ""); - - static bool - exists (const database&, const std::string& 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 table</code>, <code>db column</code>, - and <code>db 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><odb/transaction.hxx></code> - header file to make this class available in your application. - For example:</p> - - <pre class="cxx"> -#include <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& - database (); - - connection_type& - connection (); - - bool - finilized () const; - - public: - static bool - has_current (); - - static transaction& - current (); - - static void - current (transaction&); - - 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 < 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& db, person& 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& db, unsigned long id) -{ - transaction t (db.begin ()); - - auto_ptr<person> p (db.load<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& db, person& 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><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& statement); - - unsigned long long - execute (const char* statement, std::size_t length); - - database_type& - database (); - }; - - typedef details::shared_ptr<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& 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& 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 <typename T> - typename object_traits<T>::id_type - persist (const T& object); - - template <typename T> - typename object_traits<T>::id_type - persist (const object_traits<T>::const_pointer_type& object); - - template <typename T> - typename object_traits<T>::id_type - persist (T& object); - - template <typename T> - typename object_traits<T>::id_type - persist (const object_traits<T>::pointer_type& object); - </pre> - - <p>Here and in the rest of the manual, - <code>object_traits<T>::pointer_type</code> and - <code>object_traits<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<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<person> jane (new person ("Jane", "Doe", 32)); - -transaction t (db.begin ()); - -db.persist (john); -unsigned long jane_id (db.persist (jane)); - -t.commit (); - -cerr << "Jane's id: " << jane_id << 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 <typename T> - typename object_traits<T>::pointer_type - load (const typename object_traits<T>::id_type& id); - - template <typename T> - void - load (const typename object_traits<T>::id_type& id, T& 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<person> jane (db.load<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 <typename T> - void - reload (T& object); - - template <typename T> - void - reload (const object_traits<T>::pointer_type& 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 <typename T> - typename object_traits<T>::pointer_type - find (const typename object_traits<T>::id_type& id); - - template <typename T> - bool - find (const typename object_traits<T>::id_type& id, T& 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 <typename T> - void - update (const T& object); - - template <typename T> - void - update (const object_traits<T>::const_pointer_type& object); - - template <typename T> - void - update (const object_traits<T>::pointer_type& 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& 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 () < 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<bank_account> from (db.load<bank_account> (from_acc)); - -if (from->balance () < amount) - throw insufficient_funds (); - -shared_ptr<bank_account> to (db.load<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 <typename T> - void - erase (const T& object); - - template <typename T> - void - erase (const object_traits<T>::const_pointer_type& object); - - template <typename T> - void - erase (const object_traits<T>::pointer_type& object); - - template <typename T> - void - erase (const typename object_traits<T>::id_type& 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& john = ... -shared_ptr<jane> jane = ... -unsigned long joe_id = ... - -transaction t (db.begin ()); - -db.erase (john); -db.erase (jane); -db.erase<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 <typename T> - unsigned long long - erase_query (); - - template <typename T> - unsigned long long - erase_query (const odb::query<T>&); - </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<person> query; - -transaction t (db.begin ()); - -db.erase_query<person> (query::last == "Doe" && query::age < 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<employee> query; - -transaction t (db.begin ()); - -employer& e = ... // Employer object to be deleted. - -db.erase_query<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& 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&); - - 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><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&, const statement&); - - virtual void - execute (connection&, const statement&); - - virtual void - execute (connection&, const char* statement) = 0; - - virtual void - deallocate (connection&, const statement&); - }; -} - </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::<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 <odb/pgsql/tracer.hxx> -#include <odb/pgsql/database.hxx> -#include <odb/pgsql/connection.hxx> -#include <odb/pgsql/statement.hxx> - -class pgsql_tracer: public odb::pgsql::tracer -{ - virtual void - prepare (odb::pgsql::connection& c, const odb::pgsql::statement& s) - { - cerr << c.database ().db () << ": PREPARE " << s.name () - << " AS " << s.text () << endl; - } - - virtual void - execute (odb::pgsql::connection& c, const odb::pgsql::statement& s) - { - cerr << c.database ().db () << ": EXECUTE " << s.name () << endl; - } - - virtual void - execute (odb::pgsql::connection& c, const char* statement) - { - cerr << c.database ().db () << ": " << statement << endl; - } - - virtual void - deallocate (odb::pgsql::connection& c, const odb::pgsql::statement& s) - { - cerr << c.database ().db () << ": DEALLOCATE " << s.name () << 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& db = ...; -db.tracer (tracer); // Compile error. - -odb::pgsql::database& 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& - 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 not_null</code> or - <code>db 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><odb/exception.hxx></code> header file. All the - concrete ODB exceptions are defined in - <code><odb/exceptions.hxx></code> which also includes - <code><odb/exception.hxx></code>. Normally you don't - need to include either of these two headers because they are - automatically included by <code><odb/database.hxx></code>. - However, if the source file that handles ODB exceptions - does not include <code><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<person> query; - typedef odb::result<person> result; - - unsigned short age; - query q (query::first == "John" && query::age < query::_ref (age)); - - for (age = 10; age < 100; age += 10) - { - result r (db.query<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 && query::agee < 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 < 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>&&</code> (AND), - <code>||</code> (OR), and <code>!</code> (NOT). For example:</p> - - <pre class="cxx"> - typedef odb::query<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><</code></td> - <td>less than</td> - <td><code>query::age < 31</code></td> - </tr> - - <tr> - <td><code>></code></td> - <td>greater than</td> - <td><code>query::age > 31</code></td> - </tr> - - <tr> - <td><code><=</code></td> - <td>less than or equal</td> - <td><code>query::age <= 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<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") && - query::age < 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<person> (q1); // Find John. - db.query<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 < age); // By value. - query q2 (query::age < query::_val (age)); // By value. - query q3 (query::age < query::_ref (age)); // By reference. - - query q4 ("age < " + age); // Error. - query q5 ("age < " + query::_val (age)); // By value. - query q6 ("age < " + 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 <typename T> - result<T> - query (bool cache = true); - - template <typename T> - result<T> - query (const odb::query<T>&, 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<person> query; - typedef odb::result<person> result; - - result all (db.query<person> ()); - result johns (db.query<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<person> (q)); - result r1 (db.query<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& db, const query& name_query) -{ - return db.query<person> (name_query && query::age < 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<person> query; - - auto_ptr<person> p ( - db.query_one<person> ( - query::email == "jon@example.com")); - </pre> - - <p>The shortcut query functions have the following signatures:</p> - - <pre class="cxx"> - template <typename T> - typename object_traits<T>::pointer_type - query_one (); - - template <typename T> - bool - query_one (T&); - - template <typename T> - T - query_value (); - - template <typename T> - typename object_traits<T>::pointer_type - query_one (const odb::query<T>&); - - template <typename T> - bool - query_one (const odb::query<T>&, T&); - - template <typename T> - T - query_value (const odb::query<T>&); - </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 — <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<person> p = db.query_one<person> ( - query::email == "jon@example.com")) - { - ... - } - - person p; - if (db.query_one<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<person> query; - typedef odb::result<person> result; - - result johns (db.query<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 <typename T> - class result - { - public: - typedef odb::result_iterator<T> iterator; - - public: - result (); - - result (const result&); - - result& - operator= (const result&); - - void - swap (result&) - - 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<T>::iterator</code> - type, for example:</p> - - <pre class="cxx"> - result r (db.query<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& 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 <typename T> - class result_iterator - { - public: - T* - operator-> () const; - - T& - operator* () const; - - typename object_traits<T>::pointer_type - load (); - - void - load (T& x); - - typename object_traits<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<person> (query::first == "John")); - - for (result::iterator i (r.begin ()); i != r.end ();) - { - cout << i->last () << endl; // Create an object. - person& p (*i); // Reference to the same object. - cout << p.age () << 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<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<person> (query::first == "John")); - - person p; - for (result::iterator i (r.begin ()); i != r.end (); ++i) - { - i.load (p); - cout << p.last () << endl; - cout << i.age () << 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<unsigned long> set = ...; // Persons of interest. - - result r (db.query<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 << i->first () << 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 <typename T> - prepared_query<T> - prepare_query (const char* name, const odb::query<T>&); - </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<person> query; -typedef odb::prepared_query<person> prep_query; - -prep_query pq ( - db.prepare_query<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 <typename T> - struct prepared_query - { - prepared_query (); - - prepared_query (const prepared_query&) - prepared_query& operator= (const prepared_query&) - - result<T> - execute (bool cache = true); - - typename object_traits<T>::pointer_type - execute_one (); - - bool - execute_one (T& object); - - T - execute_value (); - - const char* - name () const; - - statement& - 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<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<person> query; -typedef odb::prepared_query<person> prep_query; -typedef odb::result<person> result; - -transaction t (db.begin ()); - -unsigned short age; -query q (query::age > query::_ref (age)); -prep_query pq (db.prepare_query<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<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 <typename T> - void - cache_query (const prepared_query<T>&); - - template <typename T, typename P> - void - cache_query (const prepared_query<T>&, - std::[auto|unique]_ptr<P> params); - - template <typename T> - prepared_query<T> - lookup_query (const char* name); - - template <typename T, typename P> - prepared_query<T> - lookup_query (const char* name, P*& 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 < 5; ++i) -{ - transaction t (db.begin ()); - - prep_query pq (db.lookup_query<person> ("person-age-query")); - - if (!pq) - { - pq = db.prepare_query<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<person> ("person-age-query", age_param)); - - if (!pq) - { - auto_ptr<unsigned short> p (new unsigned short); - age_param = p.get (); - query q (query::age > query::_ref (*age_param)); - pq = db.prepare_query<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&)); - </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<void (const char* name, connection&)>); - </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& c) -{ - auto_ptr<params> p (new params); - query q (query::age > query::_ref (p->age) && - query::first == query::_ref (p->first)); - prep_query pq (c.prepare_query<person> (name, q)); - c.cache_query (pq, p); -} - -db.query_factory ("person-age-name-query", &query_factory); - -for (unsigned short age (90); age > 40; age -= 10) -{ - transaction t (db.begin ()); - - params* p; - prep_query pq (db.lookup_query<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& c) - { - unique_ptr<params> p (new params); - query q (query::age > query::_ref (p->age) && - query::first == query::_ref (p->first)); - prep_query pq (c.prepare_query<person> (name, q)); - c.cache_query (pq, std::move (p)); - }); - </pre> - - Note that the <code>database::query_factory()</code> function is not - thread-safe and should be called before starting any threads that may - require this functionality. Normally, all the prepared query factories - are registered as part of the database instance creation. - - <!-- 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<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&, const std::string&); - ... -private: - friend class odb::access; - name (); - ... -}; - -#pragma db object -class person -{ - ... -private: - std::vector<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<std::string> nicknames_; - ... -}; - </pre> - - <p>The resulting database table (called <code>person_nicknames</code>) will - contain the object id column of type <code>unsigned 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<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 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<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<std::string> emails_; - ... -}; - </pre> - - <p>The resulting database table (called <code>person_emails</code>) will - contain the object id column of type <code>unsigned 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<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<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 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<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<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<person> p1 (db.load<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&) - { - 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><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&</code>. In particular, this - means that we can use <code>odb::vector</code> instance - anywhere <code>const std::vector&</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 <class T, class A = std::allocator<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<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<T, A>::iterator base () const; - }; - - // Return std::vector iterators. The begin() functions mark - // all the elements as modified. - // - typename std::vector<T, A>::iterator mbegin (); - typename std::vector<T, A>::iterator mend (); - typename std::vector<T, A>::reverse_iterator mrbegin (); - typename std::vector<T, A>::reverse_iterator mrend (); - - // Interfacing with std::vector. - // - vector (const std::vector<T, A>&); - vector (std::vector<T, A>&&); // C++11 only. - - vector& operator= (const std::vector<T, A>&); - vector& operator= (std::vector<T, A>&&); // C++11 only. - - operator const std::vector<T, A>& () const; - std::vector<T, A>& base (); - const std::vector<T, A>& base (); - - // Change tracking. - // - bool _tracking () const; - void _start () const; - void _stop () const; - void _arm (transaction&) 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 <vector> -#include <odb/vector.hxx> - -void f (const std::vector<int>&); - -odb::vector<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<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<employer> current_employer_; - - #pragma db value_not_null - std::vector<shared_ptr<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<employer> er (new employer ("Example Inc")); - shared_ptr<employee> john (new employee ("John", "Doe")); - shared_ptr<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<employee> john (db.load<employee> (john_id)); - shared_ptr<employee> jane (db.load<employee> (jane_id)); - - cout << john->employer_->name_ << endl; - cout << jane->employer_->name_ << 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<employee> query; -typedef odb::result<employee> result; - -session s; -transaction t (db.begin ()); - -result r (db.query<employee> ( - query::employer->name == "Example Inc" && query::last == "Doe")); - -for (result::iterator i (r.begin ()); i != r.end (); ++i) - cout << i->first_ << " " << i->last_ << 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<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<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<shared_ptr<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<shared_ptr<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<employee> employee_; -}; - -#pragma db object -class employee -{ - ... - - #pragma db id - unsigned long id_; - - #pragma db not_null - shared_ptr<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<position> -{ - ... - - void - fill (shared_ptr<employee> e) - { - employee_ = e; - e->positions_ = shared_from_this (); - } - -private: - weak_ptr<employee> employee_; -}; - -#pragma db object -class employee -{ - ... - -private: - friend class position; - - #pragma db not_null - shared_ptr<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<position> p (db.load<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<position> p (db.load<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<employee> employee_; -}; - -#pragma db object -class employee -{ - ... - - #pragma db not_null - shared_ptr<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<employee> employee_; -}; - -#pragma db object -class employee -{ - ... - - #pragma db id - unsigned long id_; - - #pragma db not_null - shared_ptr<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<weak_ptr<employee> > employees_ -}; - -#pragma db object -class employee -{ - ... - - #pragma db id - unsigned long id_; - - #pragma db not_null - shared_ptr<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<weak_ptr<employee> > employees_; -}; - -#pragma db object -class employee -{ - ... - - #pragma db id - unsigned long id_; - - #pragma db value_not_null unordered - std::vector<shared_ptr<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<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<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<weak_ptr<employee> > employees_; -}; - -#pragma db object -class employee -{ - ... - - #pragma db id - unsigned long id_; - - #pragma db not_null - shared_ptr<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<employee> e (db.load<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><odb/tr1/lazy-ptr.hxx></code> - header while all the others — in - <code><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<lazy_weak_ptr<employee> > employees_; -}; - -#pragma db object -class employee -{ - ... - - #pragma db not_null - lazy_shared_ptr<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<employee> e (db.load<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 <class T> -class lazy_ptr -{ -public: - // - // The eager pointer interface. - // - - // Initialization/assignment from an eager pointer to a - // transient object. - // -public: - template <class Y> lazy_ptr (const eager_ptr<Y>&); - template <class Y> lazy_ptr& operator= (const eager_ptr<Y>&); - - // 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<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<T> get_eager () const; - - // Initialization with a persistent loaded object. - // - template <class Y> lazy_ptr (database&, Y*); - template <class Y> lazy_ptr (database&, const eager_ptr<Y>&); - - template <class Y> void reset (database&, Y*); - template <class Y> void reset (database&, const eager_ptr<Y>&); - - // Initialization with a persistent unloaded object. - // - template <class ID> lazy_ptr (database&, const ID&); - - template <class ID> void reset (database&, const ID&); - - // Query object id and database of a persistent object. - // - template <class O /* = T */> - // C++11: template <class O = T> - object_traits<O>::id_type object_id () const; - - odb::database& 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<lazy_weak_ptr<employee> > employees; - -session s; -transaction t (db.begin ()); - -shared_ptr<employer> er (db.load<employer> ("Example Inc")); -employees& 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<employee>& lwp (*i); - - if (lwp.object_id<employee> () < 100) - // C++11: if (lwp.object_id () < 100) - { - shared_ptr<employee> e (lwp.load ()); // Load and lock. - cout << e->first_ << " " << e->last_ << 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<employer> er (db.load<employer> ("Example Inc")); -shared_ptr<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<employer> er (db, std::string ("Example Inc")); -shared_ptr<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 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 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><odb/core.hxx></code> header, a friend of this value type. - For example:</p> - - <pre class="cxx"> -#include <odb/core.hxx> - -#pragma db value -class basic_name -{ -public: - basic_name (const std::string& first, const std::string& 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<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 <typename T> -struct point -{ - T x; - T y; - T z; -}; - -typedef point<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 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 <utility> // std::pair - -typedef std::pair<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<person> query; -typedef odb::result<person> result; - -transaction t (db.begin ()); - -result r (db.query<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<</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 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 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 column</code>, either the <code>db value_column</code> - (<a href="#14.4.36">Section 14.4.36, "<code>value_column</code>"</a>) or - <code>db 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<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 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<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 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 <odb/nullable.hxx> - -#pragma db object -class person -{ - ... - - std::string first_; // TEXT NOT NULL - odb::nullable<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><odb/nullable.hxx></code> header file and - has the following interface:</p> - - <pre class="cxx"> -namespace odb -{ - template <typename T> - class nullable - { - public: - typedef T value_type; - - nullable (); - nullable (const T&); - nullable (const nullable&); - template <typename Y> explicit nullable (const nullable<Y>&); - - nullable& operator= (const T&); - nullable& operator= (const nullable&); - template <typename Y> nullable& operator= (const nullable<Y>&); - - void swap (nullable&); - - // Accessor interface. - // - bool null () const; - - T& get (); - const T& get () const; - - // Pointer interface. - // - operator bool_convertible () const; - - T* operator-> (); - const T* operator-> () const; - - T& operator* (); - const T& 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<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 null</code> - pragma. For example:</p> - - <pre class="cxx"> -#pragma db object -class person -{ - ... - - std::string first_; - - #pragma db null - std::auto_ptr<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<std::string> middle_; - std::string last_; -}; - -#pragma db object -class person -{ - ... - odb::nullable<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<std::vector<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& first () const; - const std::string& 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 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 — 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& r1 (p); -person& r2 (e); - -auto_ptr<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 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<employee>. -class employee: public person -{ - ... -}; - -#pragma db object // Object pointer is std::shared_ptr<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<person> p1 (new employee (...)); - shared_ptr<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<person> p; - - transaction t (db.begin ()); - p = db.load<person> (id1); // Loads employee. - p = db.load<person> (id2); // Loads contractor. - t.commit (); -} - -// Query. -// -{ - typedef odb::query<person> query; - typedef odb::result<person> result; - - transaction t (db.begin ()); - - result r (db.query<person> (query::last == "Doe")); - - for (result::iterator i (r.begin ()); i != r.end (); ++i) - { - person& p (*i); // Can be employee or contractor. - } - - t.commit (); -} - -// Update. -// -{ - shared_ptr<person> p; - shared_ptr<employee> e; - - transaction t (db.begin ()); - - e = db.load<employee> (id1); - e->temporary (false); - p = e; - db.update (p); // Updates employee. - - t.commit (); -} - -// Erase. -// -{ - shared_ptr<person> p; - - transaction t (db.begin ()); - p = db.load<person> (id1); // Loads employee. - db.erase (p); // Erases employee. - db.erase<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<person> query; -typedef odb::result<person> result; - -transaction t (db.begin ()); - -result r (db.query<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<person> p; - - transaction t (db.begin ()); - p = db.load<person> (id); // Requires two statement. - p = db.load<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<person> p = ...; - -transaction t (db.begin ()); -db.erase<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<permanent_employee> manager_; -}; - -#pragma db object -class contractor: public temporary_employee -{ - shared_ptr<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<employee> query; - -transaction t (db.begin ()); -db.erase_query<employee> (query::permanent); // Ok. -db.erase_query<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 — 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 <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<person> p (db.load<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 load</code> and - <code>db 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 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<std::string> nicknames_; - - #pragma db section(extras_) - std::shared_ptr<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><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 <typename T> - void - load (T& object, section&); - - template <typename T> - void - update (const T& object, const section&); - </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<person> p (db.load<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& - keys () const {return keys_;} - - odb::section& - keys () {return keys_;} - -private: - odb::section keys_; - - ... -}; - -auto_ptr<person> p (db.load<person> (...)); - -section& 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<person> l ( - db.load<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<char, 1024> key_type; - - const key_type& - public_key () const {return public_key_;} - - void - public_key (const key_type& k) - { - public_key_ = k; - keys_.change (); - } - - const key_type& - private_key () const {return private_key_;} - - void - private_key (const key_type& 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<person> p; - -try -{ - transaction t (db.begin ()); - p = db.load<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&) -{ - // 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<std::string> employment_history_; -}; - -transaction t (db.begin ()); - -auto_ptr<person> p (db.load<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<manager> m (db.load<manager> (...)); - -person& p (*m); -employee& e (*m); -section& 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<person> p; - -{ - transaction t (db.begin ()); - p = db.load<person> (...); - t.commit (); -} - -{ - transaction t (db.begin ()); - - try - { - db.load (*p, p->extras_); // Throws if object state has changed. - } - catch (const object_changed&) - { - 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 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<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<employer> employer_; -}; - -transaction t (db.begin ()); - -auto_ptr<employee> e (db.load<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<std::string> nicknames_; -}; - -transaction t (db.begin ()); - -auto_ptr<person> p (db.load<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<country> residence_; - shared_ptr<country> nationality_; - - shared_ptr<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 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<employee_name> query; -typedef odb::result<employee_name> result; - -transaction t (db.begin ()); - -result r (db.query<employee_name> (query::age < 31)); - -for (result::iterator i (r.begin ()); i != r.end (); ++i) -{ - const employee_name& en (*i); - cout << en.first << " " << en.last << 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 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 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 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<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 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 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 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 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<view>::member</code> expressions. - This is similar to how we can refer to object members using the - <code>odb::query<object>::member</code> expressions when - querying the database for an object. For example:</p> - - <pre class="cxx"> -typedef odb::query<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<employee_count> (query::last == "Doe")); - -cout << ec.count << 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<view></code> scope. For instance, - in the above example, we referred to the last name member as - <code>odb::query<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<...>::employee::last</code> expression. - Similarly, to refer to the employer name, we use the - <code>odb::query<...>::employer::name</code> expression. - For example:</p> - - <pre class="cxx"> -typedef odb::result<employee_employer> result; -typedef odb::query<employee_employer> query; - -transaction t (db.begin ()); - -result r (db.query<employee_employer> ( - query::employee::last == "Doe" && - query::employer::name == "Simple Tech Ltd")); - -for (result::iterator i (r.begin ()); i != r.end (); ++i) - cout << i->first << " " << i->last << " " << i->employer_name << 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<employee_country> query; -typedef odb::result<employee_country> result; - -transaction t (db.begin ()); - -result r (db.query<employee_country> ( - query::res_country::name == query::nat_country::name)); - -for (result::iterator i (r.begin ()); i != r.end (); ++i) - cout << i->first << " " << i->last << " " << i->res_country_name << 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<employee_name> query; -typedef odb::result<employee_name> result; - -transaction t (db.begin ()); - -result r (db.query<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 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<employee> ee; - shared_ptr<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<employee_employer> query; - -transaction t (db.begin ()); - -for (const employee_employer& r: - db.query<employee_employer> (query::employee::age < 31)) -{ - cout << r.ee->age () << " " << r.er->name () << 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<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<employer_view> query; - -db.query<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<employer> e; - shared_ptr<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<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<country> res; - shared_ptr<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<employee> ee; - shared_ptr<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<employee_employer> query; - -transaction t (db.begin ()); -odb::session s; - -for (const employee_employer& r: - db.query<employee_employer> (query::employee::age < 31)) -{ - assert (r.ee->employed_by_ == r.er); - cout << r.ee->age () << " " << r.er->name () << 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(&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(&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& r, country& n): res (&r), nat (&n) {} - - country* res; - country* nat; -}; - </pre> - - <p>And here is how we can use this view:</p> - - <pre class="cxx"> -typedef odb::result<employee_country> result; - -transaction t (db.begin ()); - -result r (db.query<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;} // &er or NULL. - void set_er (shared_ptr<employer> p) {er_p = p.get ();} // From cache. - - #pragma db get(&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& x) - : er_p (x.er_p == &x.er ? &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 <odb/nullable.hxx> - -#pragma db view object(object) -struct view -{ - - odb::nullable<int> n; // If 'n' is NULL, then, logically, so is 'o'. - unique_ptr<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 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 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 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 table</code> pragma - is similar to the <code>db 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<employee_leave> query; -typedef odb::result<employee_leave> result; - -transaction t (db.begin ()); - -unsigned short v_min = ... -unsigned short l_min = ... - -result r (db.query<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<employee_vacation> query; -typedef odb::result<employee_vacation> result; - -transaction t (db.begin ()); - -result r (db.query<employee_vacation> ( - (query::last == "Doe") + "AND extra.vacation_days <> 0")); - -for (result::iterator i (r.begin ()); i != r.end (); ++i) - cout << i->first << " " << i->last << " " << i->vacation_days << 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<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 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<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<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 query</code> pragma with the varying expression - specified at the query execution time. To allow this, the - <code>db 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 && (?)) -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<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<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 <> 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 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 — 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 <> 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<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 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<sequence_value> query; -typedef odb::result<sequence_value> result; - -string seq_name = ... - -result l (db.query<sequence_value> ( - "SELECT lastval('" + seq_name + "')")); - -result n (db.query<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 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 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><odb/session.hxx></code> header file to make this class - available in your application. For example:</p> - - <pre class="cxx"> -#include <odb/database.hxx> -#include <odb/session.hxx> -#include <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&); - session& operator= (const session&); - - // Current session interface. - // - public: - static session& - current (); - - static bool - has_current (); - - static void - current (session&); - - static void - reset_current (); - - static session* - current_pointer (); - - static void - current_pointer (session*); - - // Object cache interface. - // - public: - template <typename T> - struct cache_position {...}; - - template <typename T> - cache_position<T> - cache_insert (database&, - const object_traits<T>::id_type&, - const object_traits<T>::pointer_type&); - - template <typename T> - object_traits<T>::pointer_type - cache_find (database&, const object_traits<T>::id_type&) const; - - template <typename T> - void - cache_erase (const cache_position<T>&); - - template <typename T> - void - cache_erase (database&, const object_traits<T>::id_type&); - }; -} - </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<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<person> p1 (db.load<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& db, shared_ptr<const person> p) -{ - transaction t (db.begin ()); - db.persist (p); // Persisted as const pointer. - t.commit (); -} - -session s; - -shared_ptr<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<person> p (db.load<person> (id1)); // p == p1 - p->age (30); // Undefined behavior since p1 was created const. - t.commit (); -} - -shared_ptr<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<person> p (db.load<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 <typename T> - struct cache_position - { - ... - }; - - template <typename T> - static cache_position<T> - _cache_insert (odb::database&, - const typename odb::object_traits<T>::id_type&, - const typename odb::object_traits<T>::pointer_type&); - - template <typename T> - static typename odb::object_traits<T>::pointer_type - _cache_find (odb::database&, - const typename odb::object_traits<T>::id_type&); - - template <typename T> - static void - _cache_erase (const cache_position<T>&); - - // Notification functions. - // - template <typename T> - static void - _cache_persist (const cache_position<T>&); - - template <typename T> - static void - _cache_load (const cache_position<T>&); - - template <typename T> - static void - _cache_update (odb::database&, const T& obj); - - template <typename T> - static void - _cache_erase (odb::database&, - const typename odb::object_traits<T>::id_type&); -}; - </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 << "enter age for " << p.first () << " " << p.last () << 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 << "enter age for " << p.first () << " " << p.last () << endl; -unsigned short age; -cin >> age; -p.age (age); - -{ - transaction t (db.begin ()); - - try - { - db.update (p); - } - catch (const object_changed&) - { - 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 << "age is " << p.age () << ", delete?" << 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 << "age is " << p.age () << ", delete?" << endl; - else - cerr << "age changed to " << p.age () << ", still delete?" << endl; - - getline (cin, answer); - - if (answer == "yes") - { - transaction t (db.begin ()); - - try - { - db.erase (p); - done = true; - } - catch (const object_changed&) - { - 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 model 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 model 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 long 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 model 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 <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> — the changelog file. - Just for illustration, below are the contents of this changelog.</p> - - <pre class="xml"> -<changelog database="pgsql"> - <model version="1"> - <table name="person" kind="object"> - <column name="id" type="BIGINT" null="false"/> - <column name="first" type="TEXT" null="false"/> - <column name="last" type="TEXT" null="false"/> - <primary-key auto="true"> - <column name="id"/> - </primary-key> - </table> - </model> -</changelog> - </pre> - - <p>Let's say we now would like to add another data member to the - <code>person</code> class — 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"> -<changelog database="pgsql"> - <changeset version="2"> - <alter-table name="person"> - <add-column name="middle" type="TEXT" null="false"/> - </alter-table> - </changeset> - - <model version="1"> - <table name="person" kind="object"> - <column name="id" type="BIGINT" null="false"/> - <column name="first" type="TEXT" null="false"/> - <column name="last" type="TEXT" null="false"/> - <primary-key auto="true"> - <column name="id"/> - </primary-key> - </table> - </model> -</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 @@ -<changelog database="pgsql"> -<span style="color: #009E00">+ <changeset version="2"> -+ <alter-table name="person"> -+ <add-column name="middle" type="TEXT" null="false"/> -+ </alter-table> -+ </changeset> -+</span> - <model version="1"> - <table name="person" kind="object"> - <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 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 — - <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 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 — 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 ==, !=, <, >, <=, and >= operators. - // Version ordering is as follows: {1,f} < {2,t} < {2,f} < {3,t}. - }; - - class database - { - public: - ... - - schema_version - schema_version (const std::string& name = "") const; - - bool - schema_migration (const std::string& name = "") const; - - const schema_version_migration& - schema_version_migration (const std::string& name = "") const; - - // Set schema version and migration state manually. - // - void - schema_version_migration (schema_version, - bool migration, - const std::string& name = ""); - - void - schema_version_migration (const schema_version_migration&, - const std::string& name = ""); - - // Set default schema version table for all schemas. - // - void - schema_version_table (const std::string& table_name); - - // Set schema version table for a specific schema. - // - void - schema_version_table (const std::string& table_name, - const std::string& 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&, - schema_version, - const std::string& name = ""); - - static void - migrate_schema_post (database&, - schema_version, - const std::string& name = ""); - - static void - migrate_schema (database&, - schema_version, - const std::string& name = ""); - - // Data migration. - // - // Discussed in the next section. - - - // Combined schema and data migration. - // - static void - migrate (database&, - schema_version = 0, - const std::string& name = ""); - - // Schema version information. - // - static schema_version - base_version (const database&, - const std::string& name = ""); - - static schema_version - base_version (database_id, - const std::string& name = ""); - - static schema_version - current_version (const database&, - const std::string& name = ""); - - static schema_version - current_version (database_id, - const std::string& name = ""); - - static schema_version - next_version (const database&, - schema_version = 0, - const std::string& name = ""); - - static schema_version - next_version (database_id, - schema_version, - const std::string& 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 < cv) -{ - // Old schema (and data) in the database, migrate them. - // - - if (v < bv) - { - // Error: migration from this version is no longer supported. - } - - for (v = schema_catalog::next_version (db, v); - v <= 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 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& 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& p: db.query<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& p: db.query<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&, - schema_version = 0, - const std::string& name = ""); - - typedef void data_migration_function_type (database&); - - // Common (for all the databases) data migration, C++98/03 version: - // - template <schema_version v, schema_version base> - static void - data_migration_function (data_migration_function_type*, - const std::string& name = ""); - - // Common (for all the databases) data migration, C++11 version: - // - template <schema_version v, schema_version base> - static void - data_migration_function (std::function<data_migration_function_type>, - const std::string& name = ""); - - // Database-specific data migration, C++98/03 version: - // - template <schema_version v, schema_version base> - static void - data_migration_function (database&, - data_migration_function_type*, - const std::string& name = ""); - - template <schema_version v, schema_version base> - static void - data_migration_function (database_id, - data_migration_function_type*, - const std::string& name = ""); - - // Database-specific data migration, C++11 version: - // - template <schema_version v, schema_version base> - static void - data_migration_function (database&, - std::function<data_migration_function_type>, - const std::string& name = ""); - - template <schema_version v, schema_version base> - static void - data_migration_function (database_id, - std::function<data_migration_function_type>, - const std::string& name = ""); - }; - - // Static data migration function registration, C++98/03 version: - // - template <schema_version v, schema_version base> - struct data_migration_entry - { - data_migration_entry (data_migration_function_type*, - const std::string& name = ""); - - data_migration_entry (database_id, - data_migration_function_type*, - const std::string& name = ""); - }; - - // Static data migration function registration, C++11 version: - // - template <schema_version v, schema_version base> - struct data_migration_entry - { - data_migration_entry (std::function<data_migration_function_type>, - const std::string& name = ""); - - data_migration_entry (database_id, - std::function<data_migration_function_type>, - const std::string& 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& 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& db) -{ - transaction t (db.begin ()); - - for (person& p: db.query<person> ()) - { - p.gender (guess_gender (p.first ())); - db.update (p); - } - - t.commit (); -} - -static const odb::data_migration_entry<3, MYAPP_BASE_VERSION> -migrate_gender_entry (&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 <schema_version v> -using migration_entry = odb::data_migration_entry<v, MYAPP_BASE_VERSION>; - -static const migration_entry<3> -migrate_gender_entry (&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<3> -migrate_gender_entry ( - [] (odb::database& db) - { - transaction t (db.begin ()); - - for (person& p: db.query<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& 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& db) -{ - // Assume we are already in a transaction. - // - for (person& p: db.query<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 <= 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 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 <odb/nullable.hxx> - -#pragma db object -class person -{ - ... - - odb::nullable<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 <odb/core.hxx> // odb::database -#include <odb/callback.hxx> // odb::callback_event -#include <odb/nullable.hxx> - -#pragma db object callback(migrate) -class person -{ - ... - - void - migrate (odb::callback_event e, odb::database&) - { - if (e == odb::callback_event::post_load) - { - // Guess gender if not assigned. - // - if (gender_.null ()) - gender_ = guess_gender (first_); - } - } - - odb::nullable<gender> gender_; -}; - </pre> - - <p>In particular, we don't have to touch any of the accessors - or modifiers or the application logic — 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& db) -{ - typedef odb::query<person> query; - - for (person& p: db.query<person> (query::gender.is_null ())) - { - p.gender (guess_gender (p.first ())); - db.update (p); - } -} - -static const odb::data_migration_entry<4, MYAPP_BASE_VERSION> -migrate_gender_entry (&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& db) -{ - for (person& p: db.query<person> ()) - { - p.name (p.first () + " " + - p.middle () + (p.middle ().empty () ? "" : " ") + - p.last ()); - db.update (p); - } -} - -static const odb::data_migration_entry<4, MYAPP_BASE_VERSION> -migrate_name_entry (&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&); - - static void - migrate_name (odb::database&); -}; - -void person:: -migrate_gender (odb::database& db) -{ - for (person& p: db.query<person> ()) - { - p.gender_ = guess_gender (p.first_); - db.update (p); - } -} - -static const odb::data_migration_entry<3, MYAPP_BASE_VERSION> -migrate_name_entry (&migrate_gender); - -void person:: -migrate_name (odb::database& db) -{ - for (person& p: db.query<person> ()) - { - p.name_ = p.first_ + " " + - p.middle_ + (p.middle_.empty () ? "" : " ") + - p.last_; - db.update (p); - } -} - -static const odb::data_migration_entry<4, MYAPP_BASE_VERSION> -migrate_name_entry (&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<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 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&); -}; - -void employee:: -migrate_person (odb::database& db) -{ - for (person& p: db.query<person> ()) - { - employee e (p.name (), p.gender ()); - db.persist (e); - } -} - -static const odb::data_migration_entry<5, MYAPP_BASE_VERSION> -migrate_person_entry (&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 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> - - <tr> - <td><code>options</code></td> - <td>database options for a persistent class</td> - <td><a href="#14.1.16">14.1.16</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<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 <odb/callback.hxx> - -#pragma db object callback(init) -class person -{ - ... - - void - init (odb::callback_event, odb::database&); -}; - </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&); - -void -name (odb::callback_event, odb::database&) 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><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 — 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 — 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 <odb/core.hxx> -#include <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&) - { - 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 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> — <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<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> - - <h3><a name="14.1.16">14.1.16 <code>options</code></a></h3> - - <p>The <code>options</code> specifier specifies additional table - definition options that should be used for the persistent class. For - example:</p> - - <pre class="cxx"> -#pragma db object options("PARTITION BY RANGE (age)") -class person -{ - ... - - unsigned short age_; -}; - </pre> - - <p>Table definition options for a container table can be specified with - the <code>options</code> data member specifier - (<a href="#14.4.8">Section 14.4.8, "<code>options</code>"</a>). For - example:</p> - - <pre class="cxx"> -#pragma db object -class person -{ - ... - - #pragma db options("PARTITION BY RANGE (index)") - std::vector<std::string> aliases_; -}; - </pre> - - - <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<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<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 <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><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 <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 <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<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<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<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<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<shared_ptr<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<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<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<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<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<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<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<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<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 long 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& name () const; - void name (const std::string&); -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 std::string&</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&</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 — the new value - — 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& set_name (); // By-reference modifier. - void set_name (std::string const&); // 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<account>& acc () const; - void acc (std::unique_ptr<account>); - private: - #pragma db set(acc (std::move (?))) - std::unique_ptr<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<std::string> aliases_; -}; - -#pragma db object -class person -{ - ... - -public: - const name& name () const; - void name (const name&); -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<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<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>Note that if specified for the container member, then instead of the - column definition options it specifies the table definition options for - the container table (<a href="#14.1.16">Section 14.1.16, - "<code>options</code>"</a>).</p> - - <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<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<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& f, std::string const& 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& name () const; - void name (std::string const&); - - 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><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 <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<shared_ptr<person> > employees_; -}; - -#pragma db object pointer(shared_ptr) -class person -{ - ... - - #pragma db inverse(employee_) - weak_ptr<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_ = &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<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_ = &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 long 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<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<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<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<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<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<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<shared_ptr<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<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<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<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<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<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<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<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<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 test_</code> option, then the - <code>employee</code> class table will be called - <code>test_audit_hr_employees</code> and <code>employer</code> — - <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[("<name>")] \ - [unique|type("<type>")] \ - [method("<method>")] \ - [options("<index-options>")] \ - member(<name>[, "<column-options>"])... \ - members(<name>[,<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<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<int></code> would have been much more - appropriate. To add support for mapping - <code>std::vector<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<int></code>. Below is a sample - implementation:</p> - -<pre class="cxx"> -namespace odb -{ - namespace pgsql - { - template <> - class value_traits<std::vector<int>, id_string> - { - public: - typedef std::vector<int> value_type; - typedef value_type query_type; - typedef details::buffer image_type; - - static void - set_value (value_type& v, - const details::buffer& 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<char> (is.peek ()); c != '}'; is >> c) - { - v.push_back (int ()); - is >> v.back (); - } - } - } - - static void - set_image (details::buffer& b, - std::size_t& n, - bool& is_null, - const value_type& v) - { - is_null = false; - std::ostringstream os; - - os << '{'; - - for (value_type::const_iterator i (v.begin ()), e (v.end ()); - i != e;) - { - os << *i; - - if (++i != e) - os << ','; - } - - os << '}'; - - const std::string& 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<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<int> array_; -}; -</pre> - - <p>If we wanted to always map <code>std::vector<int></code> - to PostgreSQL <code>INTEGER[]</code>, then we could instead - write:</p> - -<pre class="cxx"> -typedef std::vector<int> int_vector; -#pragma db value(int_vector) type("INTEGER[]") - -#pragma db object -class object -{ - ... - - std::vector<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><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><odb/core.hxx></code>, instead of using - <code>#pragma 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 <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 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 <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 <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 <odb/callback.hxx> -#include <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&) 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_ = &transaction::current (); - tran_->callback_register (&rollback, - const_cast<object*> (this), - transaction::event_rollback, - 0, - &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& o (*static_cast<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 <typename T> -class person -{ - ... - - T first_; - T last_; -}; - -typedef person<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 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 <utility> // std::pair - -typedef std::pair<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<person> (p); // Ok. -db.reload<person> (p); // Ok. -db.erase<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 <typename T> -class base: public base_common -{ - ... - - T value; -}; - -typedef base<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 <typename I> -void -persist (I begin, I end, bool continue_failed = true); - -template <typename I> -void -update (I begin, I end, bool continue_failed = true); - -template <typename I> -void -erase (I obj_begin, I obj_end, bool continue_failed = true); - -template <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<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] {&p1, &p2}; - -db.persist (pa, pa + sizeof(pa) / sizeof(pa[0])); - - -// Vector of raw pointers to objects. -// -std::vector<person*> pv {&p1, &p2}; - -db.persist (pv.begin (), pv.end ()); - - -// Vector of smart (shared) pointers to objects. -// -std::vector<std::shared_ptr<person>> sv { - std::make_shared<person> ("John", "Doe"), - std::make_shared<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<person_entry> people; - -void -persist (odb::database& db, people& p) -{ - std::vector<person*> tmp; - tmp.reserve (p.size ()); - std::for_each (p.begin (), - p.end (), - [&tmp] (person_entry& pe) - { - tmp.push_back (&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<unsigned long> ids {1, 2}; - -db.erase<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 — 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& - exception () const; - - bool - maybe () const; - }; - - // Iteration. - // - typedef std::set<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& - 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<person> objs {{"John", "Doe"}, {"Jane", "Doe"}}; - -try -{ - db.persist (objs.begin (), objs.end ()); -} -catch (const odb::multiple_exceptions& me) -{ - for (const auto& v: me) - { - size_t p (v.position ()); - - try - { - throw v.exception (); - } - catch (const odb::object_already_persistent&) - { - cerr << p << ": duplicate id: " << objs[p].id () << endl; - } - catch (const odb::exception& e) - { - cerr << p << ": " << e.what () << 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 - <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-<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-<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& db = ... - -typedef odb::query<person> query; -typedef odb::result<person> result; - -odb::transaction t (db.begin ()); -result r (db.query<person> (query::age < 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& db = ... - -typedef odb::sqlite::query<person> query; -typedef odb::result<person> result; // odb:: not odb::sqlite:: - -odb::sqlite::transaction t (db.begin ()); -result r (db.query<person> (query::age < 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& db = ... - -{ - using namespace odb::core; - - typedef query<person> person_query; - typedef result<person> person_result; - - transaction t (db.begin ()); - person_result r (db.query<person> (person_query::age < 30)); - ... - t.commit (); -} - </pre> - - <p>A similar mechanism is available in multi-database support. Each - database runtime defines the <code>odb::<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& db = ... - -{ - using namespace odb::sqlite::core; - - typedef query<person> person_query; - typedef result<person> person_result; - - transaction t (db.begin ()); - person_result r (db.query<person> (person_query::age < 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& pg_db = ... -sl::database& sl_db = ... - -typedef pg::query<person> pg_query; -typedef sl::query<person> sl_query; -typedef odb::result<person> result; - -// First check the local cache. -// -odb::transaction t (sl_db.begin ()); // Note: using common transaction. -result r (sl_db.query<person> (sl_query::age < 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<person> (pg_query::age < 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-<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<person> -load (odb::database& db, const std::string& name) -{ - odb::transaction t (db.begin ()); - std::unique_ptr<person> p (db.find (name)); - t.commit (); - return p; -} - -odb::pgsql::database& pg_db = ... -odb::sqlite::database& sl_db = ... - -// First try the local cache. -// -std::unique_ptr<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-<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& db, unsigned short age, unsigned long limit) -{ - typedef odb::query<person> query; - typedef odb::result<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<person> pg_query; - - pg::database& pg_db (static_cast<pg::database&> (db)); - pg_query pg_q (pg_query (q) + "LIMIT" + pg_query::_val (limit)); - r = pg_db.query<person> (pg_q); - } - else - r = db.query<person> (q); - - // Handle the result up to the limit elements. - // - ... - - t.commit (); -} - -odb::pgsql::database& pg_db = ... -odb::sqlite::database& 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::<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-<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 mysql</code>" - (or "<code>-d 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 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<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 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<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<char></code>, - <code>std::vector<unsigned char></code>, - <code>char[N]</code>, <code>unsigned char[N]</code>, - <code>std::array<char, N></code>, and - <code>std::array<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 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<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<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<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<object> ("uuid = " + query::_val<odb::mysql::id_blob> (u)); -db.query<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<connection_factory> = 0); - - database (const std::string& user, - const std::string& passwd, - const std::string& db, - const std::string& host = "", - unsigned int port = 0, - const std::string* socket = 0, - const std::string& charset = "", - unsigned long client_flags = 0, - std::[auto|unique]_ptr<connection_factory> = 0); - - database (const std::string& user, - const std::string* passwd, - const std::string& db, - const std::string& host = "", - unsigned int port = 0, - const std::string* socket = 0, - const std::string& charset = "", - unsigned long client_flags = 0, - std::[auto|unique]_ptr<connection_factory> = 0); - - database (const std::string& user, - const std::string& passwd, - const std::string& db, - const std::string& host, - unsigned int port, - const std::string& socket, - const std::string& charset = "", - unsigned long client_flags = 0, - std::[auto|unique]_ptr<connection_factory> = 0); - - database (const std::string& user, - const std::string* passwd, - const std::string& db, - const std::string& host, - unsigned int port, - const std::string& socket, - const std::string& charset = "", - unsigned long client_flags = 0, - std::[auto|unique]_ptr<connection_factory> = 0); - - database (int& argc, - char* argv[], - bool erase = false, - const std::string& charset = "", - unsigned long client_flags = 0, - std::[auto|unique]_ptr<connection_factory> = 0); - - static void - print_usage (std::ostream&); - - 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><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 <login> - --password <password> - --database <name> - --host <host> - --port <integer> - --socket <socket> - --options-file <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&); - connection (database&, MYSQL*); - - MYSQL* - handle (); - }; - - typedef details::shared_ptr<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&) = 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><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&); - pooled_connection (database_type&, MYSQL*); - }; - - typedef details::shared_ptr<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 <odb/database.hxx> - -#include <odb/mysql/database.hxx> -#include <odb/mysql/connection-factory.hxx> - -int -main (int argc, char* argv[]) -{ - auto_ptr<odb::mysql::connection_factory> f ( - new odb::mysql::connection_pool_factory (20)); - - auto_ptr<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& - sqlstate () const; - - const std::string& - 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><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 <= 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<person_range> query; -typedef odb::result<person_range> result; - -transaction t (db.begin ()); - -result r ( - db.query<person_range> ( - query::_val (1) + "," + query::_val (18))); - -for (result::iterator i (r.begin ()); i != r.end (); ++i) - cerr << i->first << " " << i->last << " " << i->age << 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<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<person_min_max_age> ( - query::_val (0) + "," + query::_val (0))); - -cerr << mma.min_age << " " << mma.max_age << 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 sqlite</code>" - (or "<code>-d 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 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 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 long</code> and - <code>unsigned long 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<char, N></code> and, on Windows, - <code>std::array<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 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<char, 128> name_; -}; - </pre> - - <p>Alternatively, this can be done on the per-type basis, for example:</p> - - <pre class="cxx"> -typedef std::array<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<char, N></code>, - <code>wchar_t[N]</code>, and <code>std::array<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<char></code>, - <code>std::vector<unsigned char></code>, - <code>char[N]</code>, <code>unsigned char[N]</code>, - <code>std::array<char, N></code>, and - <code>std::array<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 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<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<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<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<object> ("uuid = " + query::_val<odb::sqlite::id_blob> (u)); -db.query<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 <odb/sqlite/blob.hxx> -#include <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& db () const; - const std::string& table () const; - const std::string& 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 <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 <odb/sqlite/blob-stream.hxx> - -namespace odb -{ - namespace sqlite - { - class blob_stream: public stream - { - public: - blob_stream (const blob&, bool rw); - }; - } -} - -#include <odb/sqlite/text-stream.hxx> - -namespace odb -{ - namespace sqlite - { - class text_stream: public stream - { - public: - text_stream (const text&, 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 <odb/sqlite/blob-stream.hxx> -#include <odb/sqlite/text-stream.hxx> - -string txt (1024 * 1024, 't'); -vector<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<object> p (db.load<object> (o.id)); - - text_stream ts (p->t, false); // Open for reading. - vector<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<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<load_b> query; - -transaction tx (db.begin ()); - -for (load_b& lb: db.query<load_b> (query::t == "test")) -{ - blob_stream bs (lb.b, false); - vector<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<object> p (db.load<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& name, - int flags = SQLITE_OPEN_READWRITE, - bool foreign_keys = true, - const std::string& vfs = "", - std::[auto|unique]_ptr<connection_factory> = 0); - -#ifdef _WIN32 - database (const std::wstring& name, - int flags = SQLITE_OPEN_READWRITE, - bool foreign_keys = true, - const std::string& vfs = "", - std::[auto|unique]_ptr<connection_factory> = 0); -#endif - - database (int& argc, - char* argv[], - bool erase = false, - int flags = SQLITE_OPEN_READWRITE, - bool foreign_keys = true, - const std::string& vfs = "", - std::[auto|unique]_ptr<connection_factory> = 0); - - static void - print_usage (std::ostream&); - - public: - const std::string& - 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><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<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 <name> - --create - --read-only - --options-file <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&, int extra_flags = 0); - connection (database&, sqlite3*); - - transaction - begin_immediate (); - - transaction - begin_exclusive (); - - sqlite3* - handle (); - }; - - typedef details::shared_ptr<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&) = 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><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&, int extra_flags = 0); - single_connection (database&, sqlite3*); - }; - - typedef details::shared_ptr<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&, int extra_flags = 0); - pooled_connection (database_type&, sqlite3*); - }; - - typedef details::shared_ptr<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 <odb/database.hxx> - -#include <odb/sqlite/database.hxx> -#include <odb/sqlite/connection-factory.hxx> - -int -main (int argc, char* argv[]) -{ - auto_ptr<odb::sqlite::connection_factory> f ( - new odb::sqlite::connection_pool_factory (20)); - - auto_ptr<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& - 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><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 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 <odb/connection.hxx> -#include <odb/transaction.hxx> -#include <odb/schema-catalog.hxx> - -odb::database& 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 pgsql</code>" - (or "<code>-d 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 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 short</code>, <code>unsigned int</code>, and - <code>unsigned long</code>/<code>unsigned long 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<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 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<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<char></code>, - <code>std::vector<unsigned char></code>, - <code>char[N]</code>, <code>unsigned char[N]</code>, - <code>std::array<char, N></code>, and - <code>std::array<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 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<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<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<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<object> ("uuid = " + query::_val<odb::pgsql::id_uuid> (u)); -db.query<object> ("buf = " + query::_val<odb::pgsql::id_bytea> (u)); -db.query<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& user, - const std::string& password, - const std::string& db, - const std::string& host = "", - unsigned int port = 0, - const std::string& extra_conninfo = "", - std::[auto|unique]_ptr<connection_factory> = 0); - - database (const std::string& user, - const std::string& password, - const std::string& db, - const std::string& host, - const std::string& socket_ext, - const std::string& extra_conninfo = "", - std::[auto|unique]_ptr<connection_factory> = 0); - - database (const std::string& conninfo, - std::[auto|unique]_ptr<connection_factory> = 0); - - database (int& argc, - char* argv[], - bool erase = false, - const std::string& extra_conninfo = "", - std::[auto|unique]_ptr<connection_factory> = 0); - - static void - print_usage (std::ostream&); - - public: - const std::string& - user () const; - - const std::string& - password () const; - - const std::string& - db () const; - - const std::string& - host () const; - - unsigned int - port () const; - - const std::string& - socket_ext () const; - - const std::string& - extra_conninfo () const; - - const std::string& - conninfo () const; - - public: - connection_ptr - connection (); - }; - } -} - </pre> - - <p>You will need to include the <code><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 <login> | --username <login> - --password <password> - --database <name> | --dbname <name> - --host <host> - --port <integer> - --options-file <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&); - connection (database&, PGconn*); - - PGconn* - handle (); - }; - - typedef details::shared_ptr<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&) = 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><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&); - pooled_connection (database_type&, PGconn*); - }; - - typedef details::shared_ptr<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 <odb/database.hxx> - -#include <odb/pgsql/database.hxx> -#include <odb/pgsql/connection-factory.hxx> - -int -main (int argc, char* argv[]) -{ - auto_ptr<odb::pgsql::connection_factory> f ( - new odb::pgsql::connection_pool_factory (20)); - - auto_ptr<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& - message () const; - - const std::string& - 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><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 oracle</code>" - (or "<code>-d 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 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 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<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 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<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<char></code>, - <code>std::vector<unsigned char></code>, - <code>char[N]</code>, <code>unsigned char[N]</code>, - <code>std::array<char, N></code>, and - <code>std::array<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 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<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<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<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<object> ("uuid = " + query::_val<odb::oracle::id_raw> (u)); -db.query<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& user, - const std::string& password, - const std::string& db, - ub2 charset = 0, - ub2 ncharset = 0, - OCIEnv* environment = 0, - std::[auto|unique]_ptr<connection_factory> = 0); - - database (const std::string& user, - const std::string& password, - const std::string& service, - const std::string& host, - unsigned int port = 0, - ub2 charset = 0, - ub2 ncharset = 0, - OCIEnv* environment = 0, - std::[auto|unique]_ptr<connection_factory> = 0); - - database (int& argc, - char* argv[], - bool erase = false, - ub2 charset = 0, - ub2 ncharset = 0, - OCIEnv* environment = 0, - std::[auto|unique]_ptr<connection_factory> = 0); - - static void - print_usage (std::ostream&); - - public: - const std::string& - user () const; - - const std::string& - password () const; - - const std::string& - db () const; - - const std::string& - service () const; - - const std::string& - 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><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 <login> - --password <password> - --database <connect-id> - --service <name> - --host <host> - --port <integer> - --options-file <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&); - connection (database&, OCISvcCtx*); - - OCISvcCtx* - handle (); - - OCIError* - error_handle (); - - details::buffer& - lob_buffer (); - }; - - typedef details::shared_ptr<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&) = 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><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&); - pooled_connection (database_type&, OCISvcCtx*); - }; - - typedef details::shared_ptr<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 <odb/database.hxx> - -#include <odb/oracle/database.hxx> -#include <odb/oracle/connection-factory.hxx> - -int -main (int argc, char* argv[]) -{ - auto_ptr<odb::oracle::connection_factory> f ( - new odb::oracle::connection_pool_factory (20)); - - auto_ptr<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& - message () const; - }; - - typedef std::vector<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><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 table</code> and <code>db 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<int> long_container_x_; - std::vector<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<int> long_container_x_; - - #pragma db table("long_class_name_cont_y") - std::vector<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 mssql</code>" - (or "<code>-d 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 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> — - to <code>NVARCHAR(256)</code>. Otherwise, <code>std::string</code> - is mapped to <code>VARCHAR(512)</code> and <code>std::wstring</code> - — to <code>NVARCHAR(512)</code>. Note also that you can - always change this mapping using the <code>db 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 short</code>, <code>unsigned int</code>, and - <code>unsigned long</code>/<code>unsigned long 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<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<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 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<char, N></code>, - <code>wchar_t[N]</code>, and <code>std::array<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<char></code>, - <code>std::vector<unsigned char></code>, - <code>char[N]</code>, <code>unsigned char[N]</code>, - <code>std::array<char, N></code>, and <code>std::array<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 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<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<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<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<object> ("uuid = " + query::_val<odb::mssql::id_binary> (u)); -db.query<object> ( - "uuid = " + query::_val<odb::mssql::id_uniqueidentifier> (u)); -db.query<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 — <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<object> query; -typedef odb::result<object> result; - -transaction t (db.begin ()); - -result r (db.query<object> (query::num < 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& user, - const std::string& password, - const std::string& db, - const std::string& server, - const std::string& driver = "", - const std::string& extra_connect_string = "", - transaction_isolation_type = isolation_read_committed, - SQLHENV environment = 0, - std::[auto|unique]_ptr<connection_factory> = 0); - - database (const std::string& user, - const std::string& password, - const std::string& db, - protocol_type protocol = protocol_auto, - const std::string& host = "", - const std::string& instance = "", - const std::string& driver = "", - const std::string& extra_connect_string = "", - transaction_isolation_type = isolation_read_committed, - SQLHENV environment = 0, - std::[auto|unique]_ptr<connection_factory> = 0); - - database (const std::string& user, - const std::string& password, - const std::string& db, - const std::string& host, - unsigned int port, - const std::string& driver = "", - const std::string& extra_connect_string = "", - transaction_isolation_type = isolation_read_committed, - SQLHENV environment = 0, - std::[auto|unique]_ptr<connection_factory> = 0); - - database (const std::string& connect_string, - transaction_isolation_type = isolation_read_committed, - SQLHENV environment = 0, - std::[auto|unique]_ptr<connection_factory> = 0); - - database (int& argc, - char* argv[], - bool erase = false, - const std::string& extra_connect_string = "", - transaction_isolation_type = isolation_read_committed, - SQLHENV environment = 0, - std::[auto|unique]_ptr<connection_factory> = 0); - - static void - print_usage (std::ostream&); - - public: - const std::string& - user () const; - - const std::string& - password () const; - - const std::string& - db () const; - - protocol_type - protocol () const; - - const std::string& - host () const; - - const std::string& - instance () const; - - unsigned int - port () const; - - const std::string& - server () const; - - const std::string& - driver () const; - - const std::string& - extra_connect_string () const; - - transaction_isolation_type - transaction_isolation () const; - - const std::string& - connect_string () const; - - SQLHENV - environment (); - - public: - connection_ptr - connection (); - }; - } -} - </pre> - - <p>You will need to include the <code><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 <login> - --password | -P <password> - --database | -d <name> - --server | -S <address> - --driver <name> - --options-file <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&); - connection (database&, SQLHDBC handle); - - SQLHDBC - handle (); - - details::buffer& - long_data_buffer (); - }; - - typedef details::shared_ptr<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&) = 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><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&); - pooled_connection (database_type&, SQLHDBC handle); - }; - - typedef details::shared_ptr<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 <odb/database.hxx> - -#include <odb/mssql/database.hxx> -#include <odb/mssql/connection-factory.hxx> - -int -main (int argc, char* argv[]) -{ - auto_ptr<odb::mssql::connection_factory> f ( - new odb::mssql::connection_pool_factory (20)); - - auto_ptr<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& - sqlstate () const; - - const std::string& - message () const; - }; - - typedef std::vector<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><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 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 <= @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<person_range> query; -typedef odb::result<person_range> result; - -transaction t (db.begin ()); - -result r ( - db.query<person_range> ( - query::_val (1) + "," + query::_val (18))); - -for (result::iterator i (r.begin ()); i != r.end (); ++i) - cerr << i->first << " " << i->last << " " << i->age << 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<person_age_range> ()); -cerr << ar.min_age << " " << ar.max_age << 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<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 - <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><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<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><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<employee> employee_; -}; - -#pragma db object -class employee -{ - ... - - #pragma db not_null - boost::shared_ptr<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<std::string> 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< - std::string, - mi::indexed_by< - mi::sequenced<>, - mi::ordered_unique<mi::identity<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< (const name&, const name&); - -#pragma db object -class person -{ - ... - - typedef - mi::multi_index_container< - name, - mi::indexed_by< - mi::ordered_unique<mi::identity<name> > - mi::ordered_non_unique< - mi::member<name, std::string, &name::first> - >, - mi::ordered_non_unique< - mi::member<name, std::string, &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 <boost/optional.hpp> - -#pragma db object -class person -{ - ... - - std::string first_; // TEXT NOT NULL - boost::optional<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><odb/boost/date-time/exceptions.hxx></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 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 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 20:45:54 GMT and - 2038-01-19 03:14:07 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 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 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><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 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 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 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 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<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><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<Employee> employee_; -}; - -#pragma db object -class Employee -{ - ... - - #pragma db not_null - QSharedPointer<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<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><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&</code>. In particular, this - means that we can use <code>QOdbList</code> instance - anywhere <code>const QList&</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 <typename T> -class QOdbList -{ - ... - - // Element access. - // - - //T& operator[] (int); - T& modify (int); - - //T& first(); - T& modifyFirst(); - - //T& last(); - T& modifyLast(); - - //T& front(); - T& modify_front(); - - //T& back(); - T& modify_back(); - - // Iterators. - // - typedef typename QList<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<T>::iterator base () const; - }; - - // Return QList iterators. The begin() functions mark all - // the elements as modified. - // - typename QList<T>::iterator mbegin (); - typename QList<T>::iterator modifyBegin (); - typename QList<T>::iterator mend (); - typename QList<T>::iterator modifyEnd (); - - // Interfacing with QList. - // - QOdbList (const QList<T>&); - QOdbList (QList<T>&&); // C++11 only. - - QOdbList& operator= (const QList<T>&); - QOdbList& operator= (QList<T>&&); - - operator const QList<T>& () const; - QList<T>& base (); - const QList<T>& base () const; - - // Change tracking. - // - bool _tracking () const; - void _start () const; - void _stop () const; - void _arm (transaction&) 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 <QtCore/QList> -#include <odb/qt/list.hxx> - -void f (const QList<int>&); - -QOdbList<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><odb/qt/list-iterator.hxx></code> and - <code><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><odb/qt/date-time/exceptions.hxx></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 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 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 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 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 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> diff --git a/doc/odb-arch.png b/doc/odb-arch.png Binary files differdeleted file mode 100644 index 511b198..0000000 --- a/doc/odb-arch.png +++ /dev/null diff --git a/doc/odb-arch.svg b/doc/odb-arch.svg deleted file mode 100644 index 368c223..0000000 --- a/doc/odb-arch.svg +++ /dev/null @@ -1,410 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="744.09448819" - height="1052.3622047" - id="svg1947" - sodipodi:version="0.32" - inkscape:version="0.46" - sodipodi:docbase="/home/boris/tmp" - sodipodi:docname="odb-arch.svg" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091" - inkscape:output_extension="org.inkscape.output.svg.inkscape"> - <defs - id="defs1949"> - <marker - inkscape:stockid="Arrow1Mstart" - orient="auto" - refY="0.0" - refX="0.0" - id="Arrow1Mstart" - style="overflow:visible"> - <path - id="path3971" - d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " - style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none" - transform="scale(0.4) translate(10,0)" /> - </marker> - <marker - inkscape:stockid="TriangleInS" - orient="auto" - refY="0.0" - refX="0.0" - id="TriangleInS" - style="overflow:visible"> - <path - id="path4105" - d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z " - style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none" - transform="scale(-0.2)" /> - </marker> - <marker - inkscape:stockid="TriangleOutS" - orient="auto" - refY="0.0" - refX="0.0" - id="TriangleOutS" - style="overflow:visible"> - <path - id="path4114" - d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z " - style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none" - transform="scale(0.2)" /> - </marker> - <marker - inkscape:stockid="Arrow2Send" - orient="auto" - refY="0.0" - refX="0.0" - id="Arrow2Send" - style="overflow:visible;"> - <path - id="path3998" - style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;" - d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " - transform="scale(0.3) rotate(180) translate(-2.3,0)" /> - </marker> - <marker - inkscape:stockid="Arrow1Send" - orient="auto" - refY="0.0" - refX="0.0" - id="Arrow1Send" - style="overflow:visible;"> - <path - id="path3980" - d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " - style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;" - transform="scale(0.2) rotate(180) translate(6,0)" /> - </marker> - <marker - inkscape:stockid="TriangleInL" - orient="auto" - refY="0.0" - refX="0.0" - id="TriangleInL" - style="overflow:visible"> - <path - id="path4099" - d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z " - style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none" - transform="scale(-0.8)" /> - </marker> - <marker - inkscape:stockid="Arrow1Lstart" - orient="auto" - refY="0.0" - refX="0.0" - id="Arrow1Lstart" - style="overflow:visible"> - <path - id="path3965" - d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " - style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none" - transform="scale(0.8) translate(12.5,0)" /> - </marker> - <marker - inkscape:stockid="Arrow2Mend" - orient="auto" - refY="0.0" - refX="0.0" - id="Arrow2Mend" - style="overflow:visible;"> - <path - id="path3992" - style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;" - d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " - transform="scale(0.6) rotate(180) translate(0,0)" /> - </marker> - <marker - inkscape:stockid="Arrow1Mend" - orient="auto" - refY="0.0" - refX="0.0" - id="Arrow1Mend" - style="overflow:visible;"> - <path - id="path3974" - d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " - style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;" - transform="scale(0.4) rotate(180) translate(10,0)" /> - </marker> - <marker - inkscape:stockid="Arrow1Lend" - orient="auto" - refY="0.0" - refX="0.0" - id="Arrow1Lend" - style="overflow:visible;"> - <path - id="path3968" - d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " - style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;" - transform="scale(0.8) rotate(180) translate(12.5,0)" /> - </marker> - <inkscape:perspective - sodipodi:type="inkscape:persp3d" - inkscape:vp_x="0 : 526.18109 : 1" - inkscape:vp_y="0 : 1000 : 0" - inkscape:vp_z="744.09448 : 526.18109 : 1" - inkscape:persp3d-origin="372.04724 : 350.78739 : 1" - id="perspective77" /> - </defs> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - gridtolerance="10000" - guidetolerance="10" - objecttolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="0.79999996" - inkscape:cx="325.54804" - inkscape:cy="759.16274" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - inkscape:window-width="1670" - inkscape:window-height="1025" - inkscape:window-x="0" - inkscape:window-y="0"> - <inkscape:grid - type="xygrid" - id="grid2450" - visible="true" - enabled="true" /> - </sodipodi:namedview> - <metadata - id="metadata1952"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - style="opacity:1"> - <rect - style="opacity:1;fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:19.11361885;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4099" - width="356.4422" - height="85.467361" - x="203.24568" - y="56.645363" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091" /> - <rect - style="opacity:1;fill:#577aa7;fill-opacity:1;fill-rule:evenodd;stroke:#5679a6;stroke-width:21.99446297;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4103" - width="153.07578" - height="33.819218" - x="-542.89569" - y="93.412643" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091" /> - <text - xml:space="preserve" - style="font-size:13.78440762px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold" - x="426.08978" - y="109.31374" - id="text4105" - sodipodi:linespacing="100%" - transform="scale(0.9475703,1.0553307)" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091"><tspan - sodipodi:role="line" - id="tspan4125" - x="426.08978" - y="109.31374">Application Code</tspan></text> - <rect - style="opacity:1;fill:#577aa7;fill-opacity:1;fill-rule:evenodd;stroke:#5679a6;stroke-width:21.14485359;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4111" - width="138.53285" - height="34.538216" - x="-357.79926" - y="93.017075" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091" /> - <text - xml:space="preserve" - style="font-size:13.70444584px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold" - x="232.20055" - y="108.67963" - id="text4113" - sodipodi:linespacing="100%" - transform="scale(0.9420736,1.0614882)" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091"><tspan - sodipodi:role="line" - id="tspan4120" - x="232.20055" - y="108.67963">Persistent Classes</tspan></text> - <rect - style="fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:25.07188988;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4061" - width="350.48395" - height="149.55804" - x="206.27087" - y="179.81218" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091" /> - <rect - style="fill:#577aa7;fill-opacity:1;fill-rule:evenodd;stroke:#5679a6;stroke-width:21.85818291;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4035" - width="153.5531" - height="33.297592" - x="-543.32996" - y="214.04765" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091" /> - <rect - style="fill:#577aa7;fill-opacity:1;fill-rule:evenodd;stroke:#5679a6;stroke-width:21.26772499;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4043" - width="137.97311" - height="35.082535" - x="-357.69339" - y="213.11913" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091" /> - <text - xml:space="preserve" - style="font-size:12.72555733px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold" - x="363.88818" - y="188.32079" - id="text4127" - sodipodi:linespacing="100%" - transform="scale(1.0051164,0.9949096)" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091"><tspan - sodipodi:role="line" - id="tspan4131" - x="363.88818" - y="188.32079">ODB</tspan></text> - <text - xml:space="preserve" - style="font-size:12.72555733px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold" - x="339.01544" - y="67.706772" - id="text4085" - sodipodi:linespacing="100%" - transform="scale(1.0051164,0.9949096)" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091"><tspan - sodipodi:role="line" - id="tspan4087" - x="339.01544" - y="67.706772">Application</tspan></text> - <text - xml:space="preserve" - style="font-size:13.78440762px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold" - x="417.64713" - y="223.02219" - id="text4165" - sodipodi:linespacing="100%" - transform="scale(0.9475703,1.0553307)" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091"><tspan - sodipodi:role="line" - id="tspan4169" - x="417.64713" - y="223.02219">ODB Common Runtime</tspan></text> - <text - xml:space="preserve" - style="font-size:13.78440762px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold" - x="246.68355" - y="223.02219" - id="text4171" - sodipodi:linespacing="100%" - transform="scale(0.9475703,1.0553307)" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091"><tspan - sodipodi:role="line" - id="tspan4177" - x="246.68355" - y="223.02219">Generated Code</tspan></text> - <rect - style="opacity:1;fill:#577aa7;fill-opacity:1;fill-rule:evenodd;stroke:#5679a6;stroke-width:28.23288155;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4179" - width="316.83414" - height="26.922894" - x="-539.57642" - y="287.25192" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091" /> - <text - xml:space="preserve" - style="font-size:13.78440762px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold" - x="331.11002" - y="289.35214" - id="text4181" - sodipodi:linespacing="100%" - transform="scale(0.9475703,1.0553307)" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091"><tspan - sodipodi:role="line" - id="tspan4185" - x="331.11002" - y="289.35214">ODB MySQL Runtime</tspan></text> - <rect - style="opacity:1;fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:22.96506691;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4207" - width="352.76007" - height="33.008934" - x="205.11604" - y="368.49875" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091" /> - <text - xml:space="preserve" - style="font-size:12.72555733px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold" - x="318.12234" - y="392.35941" - id="text4201" - sodipodi:linespacing="100%" - transform="scale(1.0051164,0.9949096)" - inkscape:export-filename="/home/boris/inkscape/odb-arch-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091"><tspan - sodipodi:role="line" - id="tspan4205" - x="318.12234" - y="392.35941">MySQL Database</tspan></text> - </g> -</svg> diff --git a/doc/odb-epilogue.1 b/doc/odb-epilogue.1 deleted file mode 100644 index e331b23..0000000 --- a/doc/odb-epilogue.1 +++ /dev/null @@ -1,153 +0,0 @@ -.\" -.\" SQL NAME TRANSFORMATIONS -.\" -.SH SQL NAME TRANSFORMATIONS -The ODB compiler provides a number of mechanisms for transforming -automatically-derived SQL names, such as tables, columns, etc., -to match a specific naming convention. At the higher level, we can -add a prefix to global names (tables and, for some databases, -indexes and/or foreign keys) with the -.B --table-prefix -option. Similarly, we can specify custom suffixes for automatically-derived -index -.RB ( --index-suffix ; -default is -.BR _i ), -foreign key -.RB ( --fkey-suffix ; -default is -.BR _fk ), -and sequence -.RB ( --sequence-suffix ; -default is -.BR _seq ) -names. Finally, we can also convert all the names to upper or lower -case with the -.B --sql-name-case -option (valid values are -.B upper -and -.BR lower ). - -At the lower level we can specify a set of regular expressions to -implement arbitrary transformations of the automatically-derived SQL -names. If we want a particular regular expression only to apply to -a specific name, for example, table or column, then we use one of the -.B --\fIkind\fB-regex -options, where -.I kind -can be -.BR table , -.BR column , -.BR index , -.BR fkey , -.BR sequence , -or -.BR statement . -On the other hand, if we want our regular expressions to apply to all SQL -names, then we use the -.B --sql-name-regex -option. - -The interaction between the higher and lower level transformations -is as follows. Prefixes and suffixes are added first. Then the -regular expression transformations are applied. Finally, if requested, -the name is converted to upper or lower case. Note also that all of -these transformations except for -.B --table-prefix -only apply to automatically-derived names. In other words, if a table, -column, etc., name was explicitly specified with a pragma, then it -is used as is, without applying any (except for the table prefix) -transformations. - -The value for the -.B --*-regex -options is a Perl-like regular expression in the form -.BI / pattern / replacement /\fR. -Any character can be used as a delimiter instead of -.B / -and the delimiter can be escaped inside -.I pattern -and -.I replacement -with a backslash -.RB ( \e ). -You can also specify multiple regular expressions by repeating these -options. - -All the regular expressions are tried in the order specified with the -name-specific expressions (for example, -.BR --table-regex) -tried first followed by the generic expressions -.RB ( --sql-name-regex ). -The first expression that matches is used. - -As an example, consider a regular expression that transforms a class -name in the form -.B CFoo -to a table name in the form -.BR FOO: - -.B --table-regex '/C(.+)/\eU$1/' - -As a more interesting example, consider the transformation of class -names that follow the upper camel case convention (for example, -.BR FooBar ) -to table names that follow the underscore-separated, all upper case -convention (for example, -.BR FOO_BAR ). -For this case we have to use separate expressions to handle one-word, -two-word, etc., names: - -.B --table-regex '/([A-z][a-z]+)/\eU$1/' - -.B --table-regex '/([A-z][a-z]+)([A-z][a-z]+)/\eU$1_$2/' - -See also the REGEX AND SHELL QUOTING section below. -.\" -.\" REGEX AND SHELL QUOTING -.\" -.SH REGEX AND SHELL QUOTING -When entering a regular expression argument in the shell command line -it is often necessary to use quoting (enclosing the argument in " " -or ' ') in order to prevent the shell from interpreting certain -characters, for example, spaces as argument separators and $ as -variable expansions. - -Unfortunately it is hard to achieve this in a manner that is portable -across POSIX shells, such as those found on GNU/Linux and UNIX, and -Windows shell. For example, if you use " " for quoting you will get -a wrong result with POSIX shells if your expression contains $. The -standard way of dealing with this on POSIX systems is to use ' ' -instead. Unfortunately, Windows shell does not remove ' ' from -arguments when they are passed to applications. As a result you may -have to use ' ' for POSIX and " " for Windows ($ is not treated as -a special character on Windows). - -Alternatively, you can save regular expression options into a file, -one option per line, and use this file with the -.B --options-file -option. With this approach you don't need to worry about shell quoting. -.\" -.\" DIAGNOSTICS -.\" -.SH DIAGNOSTICS -If the input file is not valid C++, -.B odb -will issue diagnostic messages to STDERR and exit with non-zero exit code. -.\" -.\" BUGS -.\" -.SH BUGS -Send bug reports to the odb-users@codesynthesis.com mailing list. -.\" -.\" COPYRIGHT -.\" -.SH COPYRIGHT -Copyright (c) $copyright$. - -Permission is granted to copy, distribute and/or modify this -document under the terms of the GNU Free Documentation License, -version 1.2; with no Invariant Sections, no Front-Cover Texts and -no Back-Cover Texts. Copy of the license can be obtained from -http://www.codesynthesis.com/licenses/fdl-1.3.txt diff --git a/doc/odb-epilogue.xhtml b/doc/odb-epilogue.xhtml deleted file mode 100644 index 9eba558..0000000 --- a/doc/odb-epilogue.xhtml +++ /dev/null @@ -1,126 +0,0 @@ - <h1>SQL NAME TRANSFORMATIONS</h1> - - <p>The ODB compiler provides a number of mechanisms for transforming - automatically-derived SQL names, such as tables, columns, etc., - to match a specific naming convention. At the higher level, we can - add a prefix to global names (tables and, for some databases, - indexes and/or foreign keys) with the <code><b>--table-prefix</b></code> - option. Similarly, we can specify custom suffixes for - automatically-derived - index (<code><b>--index-suffix</b></code>; default is <code><b>_i</b></code>), - foreign key (<code><b>--fkey-suffix</b></code>; default is <code><b>_fk</b></code>), and - sequence (<code><b>--sequence-suffix</b></code>; default is <code><b>_seq</b></code>) - names. Finally, we can also convert all the names to upper or lower - case with the <code><b>--sql-name-case</b></code> option (valid values - are <code><b>upper</b></code> and <code><b>lower</b></code>).</p> - - <p>At the lower level we can specify a set of regular expressions to - implement arbitrary transformations of the automatically-derived SQL - names. If we want a particular regular expression only to apply to - a specific name, for example, table or column, then we use one of the - <code><b>--</b><i>kind</i><b>-regex</b></code> options, where - <code><i>kind</i></code> can be <code><b>table</b></code>, - <code><b>column</b></code>, <code><b>index</b></code>, - <code><b>fkey</b></code>, <code><b>sequence</b></code>, or - <code><b>statement</b></code>. On the other hand, if we want our - regular expressions to apply to all SQL names, then we use the - <code><b>--sql-name-regex</b></code> option.</p> - - <p>The interaction between the higher and lower level transformations - is as follows. Prefixes and suffixes are added first. Then the - regular expression transformations are applied. Finally, if requested, - the name is converted to upper or lower case. Note also that all of - these transformations except for <code><b>--table-prefix</b></code> - only apply to automatically-derived names. In other words, if a table, - column, etc., name was explicitly specified with a pragma, then it - is used as is, without applying any (except for the table prefix) - transformations.</p> - - <p>The value for the <code><b>--*-regex</b></code> options is a Perl-like - regular expression in the form - <code><b>/</b><i>pattern</i><b>/</b><i>replacement</i><b>/</b></code>. - Any character can be used as a delimiter instead of <code><b>/</b></code> - and the delimiter can be escaped inside <code><i>pattern</i></code> and - <code><i>replacement</i></code> with a backslash (<code><b>\</b></code>). - You can also specify multiple regular expressions by repeating these - options.</p> - - <p>All the regular expressions are tried in the order specified with the - name-specific expressions (for example, <code><b>--table-regex</b></code>) - tried first followed by the generic expressions - (<code><b>--sql-name-regex</b></code>). The first expression that - matches is used.</p> - - <p>As an example, consider a regular expression that transforms a class - name in the form <code><b>CFoo</b></code> to a table name in the - form <code><b>FOO</b></code>:</p> - - <p><code><b>--table-regex '/C(.+)/\U$1/'</b></code></p> - - <p>As a more interesting example, consider the transformation of class - names that follow the upper camel case convention (for example, - <code><b>FooBar</b></code>) to table names that follow the - underscore-separated, all upper case convention (for example, - <code><b>FOO_BAR</b></code>). For this case we have to use - separate expressions to handle one-word, two-word, etc., - names:</p> - - <p><code><b>--table-regex '/([A-z][a-z]+)/\U$1/'</b></code></p> - <p><code><b>--table-regex '/([A-z][a-z]+)([A-z][a-z]+)/\U$1_$2/'</b></code></p> - - <p>See also the REGEX AND SHELL QUOTING section below.</p> - - <h1>REGEX AND SHELL QUOTING</h1> - - <p>When entering a regular expression argument in the shell - command line it is often necessary to use quoting (enclosing - the argument in <code><b>" "</b></code> or - <code><b>' '</b></code>) in order to prevent the shell - from interpreting certain characters, for example, spaces as - argument separators and <code><b>$</b></code> as variable - expansions.</p> - - <p>Unfortunately it is hard to achieve this in a manner that is - portable across POSIX shells, such as those found on - GNU/Linux and UNIX, and Windows shell. For example, if you - use <code><b>" "</b></code> for quoting you will get a - wrong result with POSIX shells if your expression contains - <code><b>$</b></code>. The standard way of dealing with this - on POSIX systems is to use <code><b>' '</b></code> instead. - Unfortunately, Windows shell does not remove <code><b>' '</b></code> - from arguments when they are passed to applications. As a result you - may have to use <code><b>' '</b></code> for POSIX and - <code><b>" "</b></code> for Windows (<code><b>$</b></code> is - not treated as a special character on Windows).</p> - - <p>Alternatively, you can save regular expression options into - a file, one option per line, and use this file with the - <code><b>--options-file</b></code> option. With this approach - you don't need to worry about shell quoting.</p> - - <h1>DIAGNOSTICS</h1> - - <p>If the input file is not valid C++, <code><b>odb</b></code> - will issue diagnostic messages to STDERR and exit with non-zero exit - code.</p> - - <h1>BUGS</h1> - - <p>Send bug reports to the - <a href="mailto:odb-users@codesynthesis.com">odb-users@codesynthesis.com</a> mailing list.</p> - - </div> - <div id="footer"> - Copyright © $copyright$. - - <div id="terms"> - Permission is granted to copy, distribute and/or modify this - document under the terms of the - <a href="http://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. - </div> - </div> -</div> -</body> -</html> diff --git a/doc/odb-flow.png b/doc/odb-flow.png Binary files differdeleted file mode 100644 index 0063317..0000000 --- a/doc/odb-flow.png +++ /dev/null diff --git a/doc/odb-flow.svg b/doc/odb-flow.svg deleted file mode 100644 index 292a121..0000000 --- a/doc/odb-flow.svg +++ /dev/null @@ -1,822 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="744.09448819" - height="1052.3622047" - id="svg1947" - sodipodi:version="0.32" - inkscape:version="0.46" - sodipodi:docbase="/home/boris/tmp" - sodipodi:docname="odb-flow.svg" - inkscape:export-filename="/home/boris/inkscape/odb-flow-t.png" - inkscape:export-xdpi="66.988091" - inkscape:export-ydpi="66.988091" - inkscape:output_extension="org.inkscape.output.svg.inkscape"> - <defs - id="defs1949"> - <linearGradient - inkscape:collect="always" - id="linearGradient8420"> - <stop - style="stop-color:#5679a6;stop-opacity:1;" - offset="0" - id="stop8422" /> - <stop - style="stop-color:#5679a6;stop-opacity:0;" - offset="1" - id="stop8424" /> - </linearGradient> - <marker - inkscape:stockid="Arrow1Mstart" - orient="auto" - refY="0.0" - refX="0.0" - id="Arrow1Mstart" - style="overflow:visible"> - <path - id="path3971" - d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " - style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none" - transform="scale(0.4) translate(10,0)" /> - </marker> - <marker - inkscape:stockid="TriangleInS" - orient="auto" - refY="0.0" - refX="0.0" - id="TriangleInS" - style="overflow:visible"> - <path - id="path4105" - d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z " - style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none" - transform="scale(-0.2)" /> - </marker> - <marker - inkscape:stockid="TriangleOutS" - orient="auto" - refY="0.0" - refX="0.0" - id="TriangleOutS" - style="overflow:visible"> - <path - id="path4114" - d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z " - style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none" - transform="scale(0.2)" /> - </marker> - <marker - inkscape:stockid="Arrow2Send" - orient="auto" - refY="0.0" - refX="0.0" - id="Arrow2Send" - style="overflow:visible;"> - <path - id="path3998" - style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;" - d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " - transform="scale(0.3) rotate(180) translate(-2.3,0)" /> - </marker> - <marker - inkscape:stockid="Arrow1Send" - orient="auto" - refY="0.0" - refX="0.0" - id="Arrow1Send" - style="overflow:visible;"> - <path - id="path3980" - d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " - style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;" - transform="scale(0.2) rotate(180) translate(6,0)" /> - </marker> - <marker - inkscape:stockid="TriangleInL" - orient="auto" - refY="0.0" - refX="0.0" - id="TriangleInL" - style="overflow:visible"> - <path - id="path4099" - d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z " - style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none" - transform="scale(-0.8)" /> - </marker> - <marker - inkscape:stockid="Arrow1Lstart" - orient="auto" - refY="0.0" - refX="0.0" - id="Arrow1Lstart" - style="overflow:visible"> - <path - id="path3965" - d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " - style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none" - transform="scale(0.8) translate(12.5,0)" /> - </marker> - <marker - inkscape:stockid="Arrow2Mend" - orient="auto" - refY="0.0" - refX="0.0" - id="Arrow2Mend" - style="overflow:visible;"> - <path - id="path3992" - style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;" - d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " - transform="scale(0.6) rotate(180) translate(0,0)" /> - </marker> - <marker - inkscape:stockid="Arrow1Mend" - orient="auto" - refY="0.0" - refX="0.0" - id="Arrow1Mend" - style="overflow:visible;"> - <path - id="path3974" - d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " - style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;" - transform="scale(0.4) rotate(180) translate(10,0)" /> - </marker> - <marker - inkscape:stockid="Arrow1Lend" - orient="auto" - refY="0.0" - refX="0.0" - id="Arrow1Lend" - style="overflow:visible;"> - <path - id="path3968" - d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " - style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;" - transform="scale(0.8) rotate(180) translate(12.5,0)" /> - </marker> - <inkscape:perspective - sodipodi:type="inkscape:persp3d" - inkscape:vp_x="0 : 526.18109 : 1" - inkscape:vp_y="0 : 1000 : 0" - inkscape:vp_z="744.09448 : 526.18109 : 1" - inkscape:persp3d-origin="372.04724 : 350.78739 : 1" - id="perspective77" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient8420" - id="linearGradient8435" - gradientUnits="userSpaceOnUse" - x1="-353.55341" - y1="187.9241" - x2="-348.25009" - y2="247.1443" /> - </defs> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - gridtolerance="10000" - guidetolerance="10" - objecttolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="0.5656854" - inkscape:cx="289.60025" - inkscape:cy="507.25221" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - inkscape:window-width="1670" - inkscape:window-height="1025" - inkscape:window-x="0" - inkscape:window-y="0"> - <inkscape:grid - type="xygrid" - id="grid2450" - visible="true" - enabled="true" /> - </sodipodi:namedview> - <metadata - id="metadata1952"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - style="opacity:1"> - <g - id="g8170" - transform="matrix(1,0,0,1.0116606,-768.75431,-37.497017)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532"> - <g - transform="translate(-136.11807,24.74874)" - id="g8121"> - <g - id="g8116"> - <rect - y="227.29126" - x="1335.5197" - height="79.829231" - width="85.835815" - id="rect8090" - style="opacity:1;fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:16.14982605;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - <path - transform="matrix(1.3965811,0,0,0.9383887,-917.47809,-64.418294)" - d="M 1670.0001,346.73715 A 17.5,29.375002 0 1 1 1635.0001,346.73715 A 17.5,29.375002 0 1 1 1670.0001,346.73715 z" - sodipodi:ry="29.375002" - sodipodi:rx="17.5" - sodipodi:cy="346.73715" - sodipodi:cx="1652.5001" - id="path8092" - style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:25;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1" - sodipodi:type="arc" /> - <rect - y="218.95593" - x="1317.5464" - height="77.754135" - width="70.679863" - id="rect8094" - style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:13.31936932;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1" /> - </g> - <rect - style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:13.31936932;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1" - id="rect8096" - width="104.53996" - height="68.249184" - x="1324.8125" - y="254.09189" /> - </g> - <rect - y="67.686447" - x="1309.4884" - height="496.32056" - width="94.349838" - id="rect7857" - style="fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:34.28735733;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - <rect - y="71.978958" - x="998.24951" - height="151.17961" - width="310.93619" - id="rect7859" - style="fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:42.29946136;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - <rect - y="210.06084" - x="1289.3831" - height="36.790138" - width="60.296692" - id="rect8168" - style="opacity:1;fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:9.18893147;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - </g> - <rect - style="opacity:1;fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00813448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect7887" - width="99.316032" - height="483.52316" - x="-638.03534" - y="53.841568" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532" /> - <text - xml:space="preserve" - style="font-size:14px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold" - x="598.49298" - y="269.48282" - id="text7889" - sodipodi:linespacing="100%" - transform="scale(0.9196215,1.0874039)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532"><tspan - y="269.48282" - x="598.49298" - sodipodi:role="line" - id="tspan7897">C++ Source</tspan></text> - <text - xml:space="preserve" - style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" - x="368.14328" - y="42.076069" - id="text7875" - sodipodi:linespacing="100%" - transform="scale(1.0051164,0.9949096)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532"><tspan - sodipodi:role="line" - id="tspan7879" - x="368.14328" - y="42.076069">Application Code</tspan></text> - <g - id="g8428" - transform="translate(-12.693573,-1.1112137)"> - <g - inkscape:export-ydpi="66.254532" - inkscape:export-xdpi="66.254532" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - id="g8378"> - <rect - style="opacity:1;fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect7867" - width="100.25191" - height="120.85359" - x="-412.69357" - y="66.473396" - transform="scale(-1,1)" /> - <rect - style="opacity:1;fill:url(#linearGradient8435);fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect7869" - width="100.25191" - height="120.85359" - x="-404.69354" - y="74.327034" - transform="scale(-1,1)" /> - <text - xml:space="preserve" - style="font-size:14px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold" - x="335.10498" - y="131.25627" - id="text7871" - sodipodi:linespacing="100%" - transform="scale(0.9420736,1.0614882)"><tspan - sodipodi:role="line" - id="tspan7873" - x="335.10498" - y="131.25627">C++ Header</tspan></text> - </g> - </g> - <g - style="opacity:1" - id="g8079" - transform="matrix(1.119828,0,0,1,-718.61594,-8.6768615)"> - <g - transform="translate(-392.50002,-507.50002)" - id="g8053"> - <rect - style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:25.7060318;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1" - id="rect8049" - width="220.90669" - height="60.921265" - x="-1453.0748" - y="775.9975" - transform="scale(-1,1)" /> - <rect - style="opacity:1;fill:#6c98d0;fill-opacity:1;fill-rule:evenodd;stroke:#6c98d0;stroke-width:25;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1" - id="rect8051" - width="217.97319" - height="58.396206" - x="-1451.4867" - y="777.53906" - transform="scale(-1,1)" /> - </g> - </g> - <rect - style="opacity:1;fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:29.97532463;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect7901" - width="244.9944" - height="146.08546" - x="222.99557" - y="389.85779" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532" /> - <rect - style="opacity:1;fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect7905" - width="100.25191" - height="120.85359" - x="-334.76144" - y="409.26379" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532" /> - <rect - style="opacity:1;fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect7907" - width="100.25191" - height="120.85359" - x="-326.76141" - y="417.11743" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532" /> - <text - xml:space="preserve" - style="font-size:13.99999993px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold" - x="252.10341" - y="454.42068" - id="text7909" - sodipodi:linespacing="100%" - transform="scale(0.9420736,1.0614882)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532"><tspan - sodipodi:role="line" - id="tspan7911" - x="252.10341" - y="454.42068">C++ Source</tspan></text> - <rect - style="opacity:1;fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect7913" - width="100.25191" - height="120.85359" - x="-463.01337" - y="409.11737" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532" /> - <rect - style="opacity:1;fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect7915" - width="100.25191" - height="120.85359" - x="-455.01334" - y="416.97101" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532" /> - <text - xml:space="preserve" - style="font-size:13.99999993px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold" - x="388.51886" - y="454.05215" - id="text7917" - sodipodi:linespacing="100%" - transform="scale(0.9420736,1.0614882)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532"><tspan - sodipodi:role="line" - id="tspan7919" - x="388.51886" - y="454.05215">C++ Header</tspan></text> - <text - xml:space="preserve" - style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" - x="279.88074" - y="397.92328" - id="text7921" - sodipodi:linespacing="100%" - transform="scale(1.0051164,0.9949096)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532"><tspan - sodipodi:role="line" - id="tspan7923" - x="279.88074" - y="397.92328">Generated Code</tspan></text> - <text - xml:space="preserve" - style="font-size:18px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold" - x="301.90692" - y="279.349" - id="text8063" - sodipodi:linespacing="100%" - transform="scale(0.9420736,1.0614882)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532"><tspan - sodipodi:role="line" - id="tspan8065" - x="301.90692" - y="279.349">ODB Compiler</tspan></text> - <rect - style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:34.97430801;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1" - id="rect8229" - width="408.91873" - height="60.921265" - x="-634.43976" - y="609.82062" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532" /> - <rect - style="opacity:1;fill:#6c98d0;fill-opacity:1;fill-rule:evenodd;stroke:#6c98d0;stroke-width:34.01371765;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1" - id="rect8231" - width="403.48856" - height="58.396206" - x="-631.5" - y="611.36218" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532" /> - <text - xml:space="preserve" - style="font-size:18px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold" - x="393.47134" - y="609.98444" - id="text8237" - sodipodi:linespacing="100%" - transform="scale(0.9420736,1.0614882)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532"><tspan - sodipodi:role="line" - id="tspan8241" - x="393.47134" - y="609.98444">C++ Compiler</tspan></text> - <rect - style="fill:#e1ecf6;fill-opacity:1;fill-rule:evenodd;stroke:#e1ecf6;stroke-width:23.70592499;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect7983" - width="251.26381" - height="89.08802" - x="391.23618" - y="738.27417" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532" /> - <text - xml:space="preserve" - style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" - x="423.17896" - y="754.1955" - id="text8003" - sodipodi:linespacing="100%" - transform="scale(1.0051164,0.9949096)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532"><tspan - sodipodi:role="line" - id="tspan8035" - x="423.17896" - y="754.1955">ODB Runtime Libraries</tspan></text> - <rect - style="fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect8021" - width="89.33712" - height="59.938789" - x="-483.33713" - y="767.36218" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532" /> - <text - xml:space="preserve" - style="font-size:14px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold" - x="438.39462" - y="754.94214" - id="text8023" - sodipodi:linespacing="100%" - transform="scale(0.9420736,1.0614882)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532"><tspan - y="754.94214" - x="438.39462" - sodipodi:role="line" - id="tspan8025">libodb</tspan></text> - <rect - style="fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect8029" - width="124.21878" - height="59.82045" - x="-639" - y="767.36218" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532" /> - <text - xml:space="preserve" - style="font-size:14px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold" - x="562.58875" - y="754.94214" - id="text8031" - sodipodi:linespacing="100%" - transform="scale(0.9420736,1.0614882)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532"><tspan - y="754.94214" - x="562.58875" - sodipodi:role="line" - id="tspan8033">libodb-mysql</tspan></text> - <rect - style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:34.97430801;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1" - id="rect8289" - width="408.91873" - height="60.921265" - x="-635.43976" - y="895.82062" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532" /> - <rect - style="fill:#6c98d0;fill-opacity:1;fill-rule:evenodd;stroke:#6c98d0;stroke-width:34.01371765;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:10;stroke-opacity:1" - id="rect8291" - width="403.48856" - height="58.396206" - x="-632.49994" - y="897.36218" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532" /> - <text - xml:space="preserve" - style="font-size:17.99999991px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold" - x="421.74155" - y="880.25012" - id="text8293" - sodipodi:linespacing="100%" - transform="scale(0.9420736,1.0614882)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532"><tspan - sodipodi:role="line" - id="tspan8299" - x="421.74155" - y="880.25012">Linker</tspan></text> - <rect - style="fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.8308351;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect8336" - width="443.51535" - height="91.56765" - x="-652.71075" - y="1007.2007" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532" /> - <text - xml:space="preserve" - style="font-size:14px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold" - x="364.17606" - y="997.43353" - id="text8338" - sodipodi:linespacing="100%" - transform="scale(0.9420736,1.0614882)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532"><tspan - sodipodi:role="line" - id="tspan8340" - x="364.17606" - y="997.43353">Application Executable</tspan></text> - <rect - style="fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.81996387;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect8311" - width="100.43195" - height="81.109138" - x="-165.93936" - y="1008.9133" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532" /> - <rect - style="fill:#5679a6;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.81996387;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect8355" - width="100.43195" - height="81.109138" - x="-157.49997" - y="1017.3622" - transform="scale(-1,1)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532" /> - <text - xml:space="preserve" - style="font-size:13.99999993px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr;text-anchor:start;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans Mono;-inkscape-font-specification:Bitstream Vera Sans Mono Bold" - x="81.206375" - y="994.76172" - id="text8315" - sodipodi:linespacing="100%" - transform="scale(0.9420736,1.0614882)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532"><tspan - sodipodi:role="line" - id="tspan8317" - x="81.206375" - y="994.76172">Database</tspan><tspan - sodipodi:role="line" - id="tspan8319" - x="81.206375" - y="1008.4662"> Schema</tspan></text> - <path - style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" - d="M 342.29515,194.11129 L 342.51612,242.94585" - id="path8437" - inkscape:connector-type="polyline" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" - d="M 261.25001,334.23715 L 261.25001,406.73715" - id="path9008" - inkscape:connector-type="polyline" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" - d="M 429.37502,334.86215 L 429.37502,406.11215" - id="path9010" - inkscape:connector-type="polyline" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" - d="M 539.06252,132.36214 L 403.49283,131.7975" - id="path10721" - inkscape:connector-type="polyline" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" - d="M 539.37502,479.23716 L 465.93752,479.23716" - id="path11861" - inkscape:connector-type="polyline" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" - d="M 590.62503,537.98716 L 590.62503,588.92466" - id="path12432" - inkscape:connector-type="polyline" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" - d="M 276.37501,537.98716 L 276.37501,589.86216" - id="path13003" - inkscape:connector-type="polyline" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" - d="M 291.25001,688.61217 L 290.62501,874.23717" - id="path13574" - inkscape:connector-type="polyline" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" - d="M 439.37502,826.11217 L 439.37502,875.48717" - id="path14145" - inkscape:connector-type="polyline" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" - d="M 576.87503,826.73717 L 577.50003,874.86217" - id="path14716" - inkscape:connector-type="polyline" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.50000000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#Arrow1Mend)" - d="M 430.82781,972.76525 L 430.88016,1006.2853" - id="path15287" - inkscape:connector-type="polyline" - inkscape:connection-start="#rect8291" - inkscape:connection-end="#rect8336" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" - d="M 209.48039,291.33848 L 129.93088,291.33849 C 123.50431,291.55946 118.76084,290.51681 114.00562,295.03073 C 110.26728,299.9874 110.72483,302.73691 110.54068,308.73995 L 111.36932,1009.0519" - id="path15858" - inkscape:connector-type="polyline" - sodipodi:nodetypes="ccccc" /> - <text - xml:space="preserve" - style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" - x="461.78705" - y="473.59952" - id="text16431" - sodipodi:linespacing="100%" - transform="scale(1.0051164,0.9949096)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532"><tspan - sodipodi:role="line" - id="tspan16437" - x="461.78705" - y="473.59952">#include</tspan></text> - <text - xml:space="preserve" - style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" - x="431.71301" - y="123.92746" - id="text16439" - sodipodi:linespacing="100%" - transform="scale(1.0051164,0.9949096)" - inkscape:export-filename="/home/boris/inkscape/g8360.png" - inkscape:export-xdpi="66.254532" - inkscape:export-ydpi="66.254532"><tspan - sodipodi:role="line" - id="tspan16441" - x="431.71301" - y="123.92746">#include</tspan></text> - </g> -</svg> diff --git a/doc/odb-prologue.1 b/doc/odb-prologue.1 deleted file mode 100644 index 24e83f4..0000000 --- a/doc/odb-prologue.1 +++ /dev/null @@ -1,84 +0,0 @@ -.\" Process this file with -.\" groff -man -Tascii odb.1 -.\" -.TH ODB 1 "February 2015" "ODB 2.4.0" -.SH NAME -odb \- object-relational mapping (ORM) compiler for C++ -.\" -.\" -.\" -.\"-------------------------------------------------------------------- -.SH SYNOPSIS -.\"-------------------------------------------------------------------- -.B odb -.B [ -.I options -.B ] -.I file -.B [ -.IR file... -.B ] -.\" -.\" -.\" -.\"-------------------------------------------------------------------- -.SH DESCRIPTION -.\"-------------------------------------------------------------------- -Given a set of C++ classes in a header file, -.B odb -generates C++ code that allows you to persist, query, and update objects -of these classes in a relational database (RDBMS). The relational -database that the generated code should target is specified with the -required -.B --database -option (see below). - - -For an input file in the form -.B name.hxx -(other file extensions can be used instead of -.BR .hxx ), -in the single-database mode (the default), the generated C++ files by -default have the following names: -.B name-odb.hxx -(header file), -.B name-odb.ixx -(inline file), and -.B name-odb.cxx -(source file). Additionally, if the -.B --generate-schema -option is specified and the -.B sql -schema format is requested (see -.BR --schema-format ), -the -.B name.sql -database schema file is generated. If the -.B separate -schema format is requested, the database creation code is generated into -the separate -.B name-schema.cxx -file. - - -In the multi-database mode (see the -.B --multi-database -option below), the generated files corresponding to the -.B common -database have the same names as in the single-database mode. For other -databases, the file names include the database name: -.BR name-odb-\fIdb\fB.hxx , -.BR name-odb-\fIdb\fB.ixx , -.BR name-odb-\fIdb\fB.cxx , -.BR name-\fIdb\fB.sql , -and -.B name-schema-\fIdb\fB.cxx -(where -.I db -is the database name). -.\" -.\" -.\" -.\"-------------------------------------------------------------------- -.SH OPTIONS -.\"-------------------------------------------------------------------- diff --git a/doc/odb-prologue.xhtml b/doc/odb-prologue.xhtml deleted file mode 100644 index b8cc694..0000000 --- a/doc/odb-prologue.xhtml +++ /dev/null @@ -1,88 +0,0 @@ -<!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>ODB 2.4.0 Compiler Command Line Manual</title> - - <meta name="copyright" content="© $copyright$"/> - <meta name="keywords" content="odb,object,relational,mapping,compiler,c++"/> - <meta name="description" content="ODB Compiler Command Line Manual"/> - - <link rel="stylesheet" type="text/css" href="default.css" /> - -<style type="text/css"> - - #synopsis { - list-style-type: none; - } - - #synopsis li { - padding-top : 0.0em; - padding-bottom : 0.0em; - } - - .options { - margin: 1em 0 1em 0; - } - - .options dt { - margin: 1em 0 0 0; - } - - .options dd { - margin: .1em 0 0 4.5em; - } - -</style> -</head> - -<body> -<div id="container"> - <div id="content"> - - <h1>NAME</h1> - - <p>odb - object-relational mapping (ORM) compiler for C++</p> - - <h1>SYNOPSIS</h1> - - <dl id="synopsis"> - <dt><code><b>odb</b> [<i>options</i>] <i>file</i> [<i>file</i>...]</code></dt> - </dl> - - <h1>DESCRIPTION</h1> - - <p>Given a set of C++ classes in a header file, <code><b>odb</b></code> - generates C++ code that allows you to persist, query, and update objects - of these classes in a relational database (RDBMS). The relational - database that the generated code should target is specified with the - required <code><b>--database</b></code> option (see below).</p> - - <p>For an input file in the form <code><b>name.hxx</b></code> (other - file extensions can be used instead of <code><b>.hxx</b></code>), - in the single-database mode (the default), the generated C++ files - by default have the following names: - <code><b>name-odb.hxx</b></code> (header file), - <code><b>name-odb.ixx</b></code> (inline file), and - <code><b>name-odb.cxx</b></code> (source file). - - Additionally, if the <code><b>--generate-schema</b></code> option is - specified and the <code><b>sql</b></code> schema format is requested (see - <code><b>--schema-format</b></code>), the <code><b>name.sql</b></code> - database schema file is generated. If the <code><b>separate</b></code> - schema format is requested, the database creation code is generated - into the separate <code><b>name-schema.cxx</b></code> file.</p> - - <p>In the multi-database mode (see the <code><b>--multi-database</b></code> - option below), the generated files corresponding to the - <code><b>common</b></code> database have the same names as in the - single-database mode. For other databases, the file names include - the database name: - <code><b>name-odb-</b><i>db</i><b>.hxx</b></code>, - <code><b>name-odb-</b><i>db</i><b>.ixx</b></code>, - <code><b>name-odb-</b><i>db</i><b>.cxx</b></code>, - <code><b>name-</b><i>db</i><b>.sql</b></code>, and - <code><b>name-schema-</b><i>db</i><b>.cxx</b></code> - (where <code><i>db</i></code> is the database name).</p> - - <h1>OPTIONS</h1> diff --git a/doc/pregenerated/odb.1 b/doc/pregenerated/odb.1 deleted file mode 100644 index 42d81d0..0000000 --- a/doc/pregenerated/odb.1 +++ /dev/null @@ -1,799 +0,0 @@ -.\" Process this file with -.\" groff -man -Tascii odb.1 -.\" -.TH ODB 1 "February 2015" "ODB 2.4.0" -.SH NAME -odb \- object-relational mapping (ORM) compiler for C++ -.\" -.\" -.\" -.\"-------------------------------------------------------------------- -.SH SYNOPSIS -.\"-------------------------------------------------------------------- -.B odb -.B [ -.I options -.B ] -.I file -.B [ -.IR file... -.B ] -.\" -.\" -.\" -.\"-------------------------------------------------------------------- -.SH DESCRIPTION -.\"-------------------------------------------------------------------- -Given a set of C++ classes in a header file, -.B odb -generates C++ code that allows you to persist, query, and update objects -of these classes in a relational database (RDBMS). The relational -database that the generated code should target is specified with the -required -.B --database -option (see below). - - -For an input file in the form -.B name.hxx -(other file extensions can be used instead of -.BR .hxx ), -in the single-database mode (the default), the generated C++ files by -default have the following names: -.B name-odb.hxx -(header file), -.B name-odb.ixx -(inline file), and -.B name-odb.cxx -(source file). Additionally, if the -.B --generate-schema -option is specified and the -.B sql -schema format is requested (see -.BR --schema-format ), -the -.B name.sql -database schema file is generated. If the -.B separate -schema format is requested, the database creation code is generated into -the separate -.B name-schema.cxx -file. - - -In the multi-database mode (see the -.B --multi-database -option below), the generated files corresponding to the -.B common -database have the same names as in the single-database mode. For other -databases, the file names include the database name: -.BR name-odb-\fIdb\fB.hxx , -.BR name-odb-\fIdb\fB.ixx , -.BR name-odb-\fIdb\fB.cxx , -.BR name-\fIdb\fB.sql , -and -.B name-schema-\fIdb\fB.cxx -(where -.I db -is the database name). -.\" -.\" -.\" -.\"-------------------------------------------------------------------- -.SH OPTIONS -.\"-------------------------------------------------------------------- -.IP "\fB--help\fR" -Print usage information and exit\. -.IP "\fB--version\fR" -Print version and exit\. -.IP "\fB-I\fR \fIdir\fR" -Add \fIdir\fR to the beginning of the list of directories to be searched for -included header files\. -.IP "\fB-D\fR \fIname\fR[=\fIdef\fR]" -Define macro \fIname\fR with definition \fIdef\fR\. If definition is omitted, -define \fIname\fR to be 1\. -.IP "\fB-U\fR \fIname\fR" -Cancel any previous definitions of macro \fIname\fR, either built-in or -provided with the \fB-D\fR option\. -.IP "\fB--database\fR|\fB-d\fR \fIdb\fR" -Generate code for the \fIdb\fR database\. Valid values are \fBmssql\fR, -\fBmysql\fR, \fBoracle\fR, \fBpgsql\fR, \fBsqlite\fR, and \fBcommon\fR -(multi-database mode only)\. -.IP "\fB--multi-database\fR|\fB-m\fR \fItype\fR" -Enable multi-database support and specify its type\. Valid values for this -option are \fBstatic\fR and \fBdynamic\fR\. - -In the multi-database mode, options that determine the kind (for example, -\fB--schema-format\fR), names (for example, \fB--odb-file-suffix\fR), or -content (for example, prologue and epilogue options) of the output files can -be prefixed with the database name followed by a colon, for example, -\fBmysql:value\fR\. This restricts the value of such an option to only apply -to generated files corresponding to this database\. -.IP "\fB--default-database\fR \fIdb\fR" -When static multi-database support is used, specify the database that should -be made the default\. When dynamic multi-database support is used, -\fBcommon\fR is always made the default database\. -.IP "\fB--generate-query\fR|\fB-q\fR" -Generate query support code\. Without this support you cannot use views and -can only load objects via their ids\. -.IP "\fB--generate-prepared\fR" -Generate prepared query execution support code\. -.IP "\fB--omit-unprepared\fR" -Omit un-prepared (once-off) query execution support code\. -.IP "\fB--generate-session\fR|\fB-e\fR" -Generate session support code\. With this option session support will be -enabled by default for all the persistent classes except those for which it -was explicitly disabled using the \fBdb session\fR pragma\. -.IP "\fB--generate-schema\fR|\fB-s\fR" -Generate the database schema\. The database schema contains SQL statements -that create database tables necessary to store persistent classes defined in -the file being compiled\. Note that by applying this schema, all the existing -information stored in such tables will be lost\. - -Depending on the database being used (\fB--database\fR option), the schema is -generated either as a standalone SQL file or embedded into the generated C++ -code\. By default the SQL file is generated for the MySQL, PostgreSQL, Oracle, -and Microsoft SQL Server databases and the schema is embedded into the C++ -code for the SQLite database\. Use the \fB--schema-format\fR option to alter -the default schema format\. - -If database schema evolution support is enabled (that is, the object model -version is specified), then this option also triggers the generation of -database schema migration statements, again either as standalong SQL files or -embedded into the generated C++ code\. You can suppress the generation of -schema migration statements by specifying the \fB--suppress-migration\fR -option\. -.IP "\fB--generate-schema-only\fR" -Generate only the database schema\. Note that this option is only valid when -generating schema as a standalone SQL file (see \fB--schema-format\fR for -details)\. -.IP "\fB--suppress-migration\fR" -Suppress the generation of database schema migration statements\. -.IP "\fB--suppress-schema-version\fR" -Suppress the generation of schema version table\. If you specify this option -then you are also expected to manually specify the database schema version and -migration state at runtime using the \fBodb::database::schema_version()\fR -function\. -.IP "\fB--schema-version-table\fR \fIname\fR" -Specify the alternative schema version table name instead of the default -\fBschema_version\fR\. If you specify this option then you are also expected -to manually specify the schema version table name at runtime using the -\fBodb::database::schema_version_table()\fR function\. The table name can be -qualified\. -.IP "\fB--schema-format\fR \fIformat\fR" -Generate the database schema in the specified format\. Pass \fBsql\fR as -\fIformat\fR to generate the database schema as a standalone SQL file or pass -\fBembedded\fR to embed the schema into the generated C++ code\. The -\fBseparate\fR value is similar to \fBembedded\fR except the schema creation -code is generated into a separate C++ file (\fBname-schema\.cxx\fR by -default)\. This value is primarily useful if you want to place the schema -creation functionality into a separate program or library\. Repeat this option -to generate the same database schema in multiple formats\. -.IP "\fB--omit-drop\fR" -Omit \fBDROP\fR statements from the generated database schema\. -.IP "\fB--omit-create\fR" -Omit \fBCREATE\fR statements from the generated database schema\. -.IP "\fB--schema-name\fR \fIname\fR" -Use \fIname\fR as the database schema name\. Schema names are primarily used -to distinguish between multiple embedded schemas in the schema catalog\. They -are not to be confused with database schemas (database namespaces) which are -specified with the \fB--schema\fR option\. If this option is not specified, -the empty name, which is the default schema name, is used\. -.IP "\fB--fkeys-deferrable-mode\fR \fIm\fR" -Use constraint checking mode \fIm\fR in foreign keys generated for object -relationships\. Valid values for this option are \fBnot_deferrable\fR, -\fBimmediate\fR, and \fBdeferred\fR (default)\. MySQL and SQL Server do not -support deferrable foreign keys and for these databases such keys are -generated commented out\. Other foreign keys generated by the ODB compiler -(such as the ones used to support containers and polymorphic hierarchies) are -always generated as not deferrable\. - -Note also that if you use either \fBnot_deferrable\fR or \fBimmediate\fR mode, -then the order in which you persist, update, and erase objects within a -transaction becomes important\. -.IP "\fB--default-pointer\fR \fIptr\fR" -Use \fIptr\fR as the default pointer for persistent objects and views\. -Objects and views that do not have a pointer assigned with the \fBdb -pointer\fR pragma will use this pointer by default\. The value of this option -can be '\fB*\fR' which denotes the raw pointer and is the default, or -qualified name of a smart pointer class template, for example, -\fBstd::shared_ptr\fR\. In the latter case, the ODB compiler constructs the -object or view pointer by adding a single template argument of the object or -view type to the qualified name, for example \fBstd::shared_ptr<object>\fR\. -The ODB runtime uses object and view pointers to return, and, in case of -objects, pass and cache dynamically allocated instances of object and view -types\. - -Except for the raw pointer and the standard smart pointers defined in the -\fB<memory>\fR header file, you are expected to include the definition of the -default pointer at the beginning of the generated header file\. There are two -common ways to achieve this: you can either include the necessary header in -the file being compiled or you can use the \fB--hxx-prologue\fR option to add -the necessary \fB#include\fR directive to the generated code\. -.IP "\fB--session-type\fR \fItype\fR" -Use \fItype\fR as the alternative session type instead of the default -\fBodb::session\fR\. This option can be used to specify a custom session -implementation to be use by the persistent classes\. Note that you will also -need to include the definition of the custom session type into the generated -header file\. This is normally achieved with the \fB--hxx-prologue*\fR -options\. -.IP "\fB--profile\fR|\fB-p\fR \fIname\fR" -Specify a profile that should be used during compilation\. A profile is an -options file\. The ODB compiler first looks for a database-specific version -with the name constructed by appending the -\fB-\fR\fIdatabase\fR\fB\.options\fR suffix to \fIname\fR, where -\fIdatabase\fR is the database name as specified with the \fB--database\fR -option\. If this file is not found, then the ODB compiler looks for a -database-independant version with the name constructed by appending just the -\fB\.options\fR suffix\. - -The profile options files are searched for in the same set of directories as -C++ headers included with the \fB#include <\.\.\.>\fR directive (built-in -paths plus those specified with the \fB-I\fR options)\. The options file is -first searched for in the directory itself and then in its \fBodb/\fR -subdirectory\. - -For the format of the options file refer to the \fB--options-file\fR option -below\. You can repeat this option to specify more than one profile\. -.IP "\fB--at-once\fR" -Generate code for all the input files as well as for all the files that they -include at once\. The result is a single set of source/schema files that -contain all the generated code\. If more than one input file is specified -together with this option, then the \fB--input-name\fR option must also be -specified in order to provide the base name for the output files\. In this -case, the directory part of such a base name is used as the location of the -combined file\. This can be important for the \fB#include\fR directive -resolution\. -.IP "\fB--schema\fR \fIschema\fR" -Specify a database schema (database namespace) that should be assigned to the -persistent classes in the file being compiled\. Database schemas are not to be -confused with database schema names (schema catalog names) which are specified -with the \fB--schema-name\fR option\. -.IP "\fB--export-symbol\fR \fIsymbol\fR" -Insert \fIsymbol\fR in places where DLL export/import control statements -(\fB__declspec(dllexport/dllimport)\fR) are necessary\. See also the -\fB--extern-symbol\fR option below\. -.IP "\fB--extern-symbol\fR \fIsymbol\fR" -If \fIsymbol\fR is defined, insert it in places where a template instantiation -must be declared \fBextern\fR\. This option is normally used together with -\fB--export-symbol\fR when both multi-database support and queries are -enabled\. -.IP "\fB--std\fR \fIversion\fR" -Specify the C++ standard that should be used during compilation\. Valid values -are \fBc++98\fR (default), \fBc++11\fR, \fBc++14\fR, \fBc++17\fR, and -\fBc++20\fR\. -.IP "\fB--warn-hard-add\fR" -Warn about hard-added data members\. -.IP "\fB--warn-hard-delete\fR" -Warn about hard-deleted data members and persistent classes\. -.IP "\fB--warn-hard\fR" -Warn about both hard-added and hard-deleted data members and persistent -classes\. -.IP "\fB--output-dir\fR|\fB-o\fR \fIdir\fR" -Write the generated files to \fIdir\fR instead of the current directory\. -.IP "\fB--input-name\fR \fIname\fR" -Use \fIname\fR instead of the input file to derive the names of the generated -files\. If the \fB--at-once\fR option is specified, then the directory part of -\fIname\fR is used as the location of the combined file\. Refer to the -\fB--at-once\fR option for details\. -.IP "\fB--changelog\fR \fIfile\fR" -Read/write changelog from/to \fIfile\fR instead of the default changelog -file\. The default changelog file name is derived from the input file name and -it is placed into the same directory as the input file\. Note that the -\fB--output-dir\fR option does not affect the changelog file location\. In -other words, by default, the changelog file is treated as another input rather -than output even though the ODB compiler may modify it\. Use the -\fB--changelog-in\fR and \fB--changelog-out\fR options to specify different -input and output chaneglog files\. -.IP "\fB--changelog-in\fR \fIfile\fR" -Read changelog from \fIfile\fR instead of the default changelog file\. If this -option is specified, then you must also specify the output chanegelog file -with \fB--changelog-out\fR\. -.IP "\fB--changelog-out\fR \fIfile\fR" -Write changelog to \fIfile\fR instead of the default changelog file\. If this -option is specified, then you must also specify the input chanegelog file with -\fB--changelog-in\fR\. -.IP "\fB--changelog-dir\fR \fIdir\fR" -Use \fIdir\fR instead of the input file directory as the changelog file -directory\. This directory is also added to changelog files specified with the -\fB--changelog\fR, \fB--changelog-in\fR, and \fB--changelog-in\fR options -unless they are absolute paths\. -.IP "\fB--init-changelog\fR" -Force re-initialization of the changelog even if one exists (all the existing -change history will be lost)\. This option is primarily useful for automated -testing\. -.IP "\fB--odb-file-suffix\fR \fIsuffix\fR" -Use \fIsuffix\fR to construct the names of the generated C++ files\. In the -single-database mode the default value for this option is \fB-odb\fR\. In the -multi-database mode it is \fB-odb\fR for the files corresponding to the -\fBcommon\fR database and \fB-odb-\fR\fIdb\fR\fR (where \fIdb\fR is the -database name) for other databases\. -.IP "\fB--sql-file-suffix\fR \fIsuffix\fR" -Use \fIsuffix\fR to construct the name of the generated schema SQL file\. In -the single-database mode by default no suffix is used\. In the multi-database -mode the default value for this option is \fB-\fR\fIdb\fR\fR (where \fIdb\fR -is the database name)\. -.IP "\fB--schema-file-suffix\fR \fIsuffix\fR" -Use \fIsuffix\fR to construct the name of the generated schema C++ source -file\. In the single-database mode the default value for this option is -\fB-schema\fR\. In the multi-database mode it is \fB-schema-\fR\fIdb\fR\fR -(where \fIdb\fR is the database name)\. See the \fB--schema-format\fR option -for details\. -.IP "\fB--changelog-file-suffix\fR \fIsfx\fR" -Use \fIsfx\fR to construct the name of the changelog file\. In the -single-database mode by default no suffix is used\. In the multi-database mode -the default value for this option is \fB-\fR\fIdb\fR\fR (where \fIdb\fR is the -database name)\. -.IP "\fB--hxx-suffix\fR \fIsuffix\fR" -Use \fIsuffix\fR instead of the default \fB\.hxx\fR to construct the name of -the generated C++ header file\. -.IP "\fB--ixx-suffix\fR \fIsuffix\fR" -Use \fIsuffix\fR instead of the default \fB\.ixx\fR to construct the name of -the generated C++ inline file\. -.IP "\fB--cxx-suffix\fR \fIsuffix\fR" -Use \fIsuffix\fR instead of the default \fB\.cxx\fR to construct the name of -the generated C++ source file\. -.IP "\fB--sql-suffix\fR \fIsuffix\fR" -Use \fIsuffix\fR instead of the default \fB\.sql\fR to construct the name of -the generated database schema file\. -.IP "\fB--changelog-suffix\fR \fIsuffix\fR" -Use \fIsuffix\fR instead of the default \fB\.xml\fR to construct the name of -the changelog file\. -.IP "\fB--hxx-prologue\fR \fItext\fR" -Insert \fItext\fR at the beginning of the generated C++ header file\. -.IP "\fB--ixx-prologue\fR \fItext\fR" -Insert \fItext\fR at the beginning of the generated C++ inline file\. -.IP "\fB--cxx-prologue\fR \fItext\fR" -Insert \fItext\fR at the beginning of the generated C++ source file\. -.IP "\fB--schema-prologue\fR \fItext\fR" -Insert \fItext\fR at the beginning of the generated schema C++ source file\. -.IP "\fB--sql-prologue\fR \fItext\fR" -Insert \fItext\fR at the beginning of the generated database schema file\. -.IP "\fB--migration-prologue\fR \fItext\fR" -Insert \fItext\fR at the beginning of the generated database migration file\. -.IP "\fB--sql-interlude\fR \fItext\fR" -Insert \fItext\fR after all the \fBDROP\fR and before any \fBCREATE\fR -statements in the generated database schema file\. -.IP "\fB--hxx-epilogue\fR \fItext\fR" -Insert \fItext\fR at the end of the generated C++ header file\. -.IP "\fB--ixx-epilogue\fR \fItext\fR" -Insert \fItext\fR at the end of the generated C++ inline file\. -.IP "\fB--cxx-epilogue\fR \fItext\fR" -Insert \fItext\fR at the end of the generated C++ source file\. -.IP "\fB--schema-epilogue\fR \fItext\fR" -Insert \fItext\fR at the end of the generated schema C++ source file\. -.IP "\fB--sql-epilogue\fR \fItext\fR" -Insert \fItext\fR at the end of the generated database schema file\. -.IP "\fB--migration-epilogue\fR \fItext\fR" -Insert \fItext\fR at the end of the generated database migration file\. -.IP "\fB--hxx-prologue-file\fR \fIfile\fR" -Insert the content of \fIfile\fR at the beginning of the generated C++ header -file\. -.IP "\fB--ixx-prologue-file\fR \fIfile\fR" -Insert the content of \fIfile\fR at the beginning of the generated C++ inline -file\. -.IP "\fB--cxx-prologue-file\fR \fIfile\fR" -Insert the content of \fIfile\fR at the beginning of the generated C++ source -file\. -.IP "\fB--schema-prologue-file\fR \fIfile\fR" -Insert the content of \fIfile\fR at the beginning of the generated schema C++ -source file\. -.IP "\fB--sql-prologue-file\fR \fIfile\fR" -Insert the content of \fIfile\fR at the beginning of the generated database -schema file\. -.IP "\fB--migration-prologue-file\fR \fIf\fR" -Insert the content of file \fIf\fR at the beginning of the generated database -migration file\. -.IP "\fB--sql-interlude-file\fR \fIfile\fR" -Insert the content of \fIfile\fR after all the \fBDROP\fR and before any -\fBCREATE\fR statements in the generated database schema file\. -.IP "\fB--hxx-epilogue-file\fR \fIfile\fR" -Insert the content of \fIfile\fR at the end of the generated C++ header file\. -.IP "\fB--ixx-epilogue-file\fR \fIfile\fR" -Insert the content of \fIfile\fR at the end of the generated C++ inline file\. -.IP "\fB--cxx-epilogue-file\fR \fIfile\fR" -Insert the content of \fIfile\fR at the end of the generated C++ source file\. -.IP "\fB--schema-epilogue-file\fR \fIfile\fR" -Insert the content of \fIfile\fR at the end of the generated schema C++ source -file\. -.IP "\fB--sql-epilogue-file\fR \fIfile\fR" -Insert the content of \fIfile\fR at the end of the generated database schema -file\. -.IP "\fB--migration-epilogue-file\fR \fIf\fR" -Insert the content of file \fIf\fR at the end of the generated database -migration file\. -.IP "\fB--odb-prologue\fR \fItext\fR" -Compile \fItext\fR before the input header file\. This option allows you to -add additional declarations, such as custom traits specializations, to the ODB -compilation process\. -.IP "\fB--odb-prologue-file\fR \fIfile\fR" -Compile \fIfile\fR contents before the input header file\. Prologue files are -compiled after all the prologue text fragments (\fB--odb-prologue\fR option)\. -.IP "\fB--odb-epilogue\fR \fItext\fR" -Compile \fItext\fR after the input header file\. This option allows you to add -additional declarations, such as custom traits specializations, to the ODB -compilation process\. -.IP "\fB--odb-epilogue-file\fR \fIfile\fR" -Compile \fIfile\fR contents after the input header file\. Epilogue files are -compiled after all the epilogue text fragments (\fB--odb-epilogue\fR option)\. -.IP "\fB--table-prefix\fR \fIprefix\fR" -Add \fIprefix\fR to table names and, for databases that have global index -and/or foreign key names, to those names as well\. The prefix is added to both -names that were specified with the \fBdb table\fR and \fBdb index\fR pragmas -and those that were automatically derived from class and data member names\. -If you require a separator, such as an underscore, between the prefix and the -name, then you should include it into the prefix value\. -.IP "\fB--index-suffix\fR \fIsuffix\fR" -Use \fIsuffix\fR instead of the default \fB_i\fR to construct index names\. -The suffix is only added to names that were automatically derived from data -member names\. If you require a separator, such as an underscore, between the -name and the suffix, then you should include it into the suffix value\. -.IP "\fB--fkey-suffix\fR \fIsuffix\fR" -Use \fIsuffix\fR instead of the default \fB_fk\fR to construct foreign key -names\. If you require a separator, such as an underscore, between the name -and the suffix, then you should include it into the suffix value\. -.IP "\fB--sequence-suffix\fR \fIsuffix\fR" -Use \fIsuffix\fR instead of the default \fB_seq\fR to construct sequence -names\. If you require a separator, such as an underscore, between the name -and the suffix, then you should include it into the suffix value\. -.IP "\fB--sql-name-case\fR \fIcase\fR" -Convert all automatically-derived SQL names to upper or lower case\. Valid -values for this option are \fBupper\fR and \fBlower\fR\. -.IP "\fB--table-regex\fR \fIregex\fR" -Add \fIregex\fR to the list of regular expressions that is used to transform -automatically-derived table names\. See the SQL NAME TRANSFORMATIONS section -below for details\. -.IP "\fB--column-regex\fR \fIregex\fR" -Add \fIregex\fR to the list of regular expressions that is used to transform -automatically-derived column names\. See the SQL NAME TRANSFORMATIONS section -below for details\. -.IP "\fB--index-regex\fR \fIregex\fR" -Add \fIregex\fR to the list of regular expressions that is used to transform -automatically-derived index names\. See the SQL NAME TRANSFORMATIONS section -below for details\. -.IP "\fB--fkey-regex\fR \fIregex\fR" -Add \fIregex\fR to the list of regular expressions that is used to transform -automatically-derived foreign key names\. See the SQL NAME TRANSFORMATIONS -section below for details\. -.IP "\fB--sequence-regex\fR \fIregex\fR" -Add \fIregex\fR to the list of regular expressions that is used to transform -automatically-derived sequence names\. See the SQL NAME TRANSFORMATIONS -section below for details\. -.IP "\fB--statement-regex\fR \fIregex\fR" -Add \fIregex\fR to the list of regular expressions that is used to transform -automatically-derived prepared statement names\. See the SQL NAME -TRANSFORMATIONS section below for details\. -.IP "\fB--sql-name-regex\fR \fIregex\fR" -Add \fIregex\fR to the list of regular expressions that is used to transform -all automatically-derived SQL names\. See the SQL NAME TRANSFORMATIONS section -below for details\. -.IP "\fB--sql-name-regex-trace\fR" -Trace the process of applying regular expressions specified with the SQL name -\fB--*-regex\fR options\. Use this option to find out why your regular -expressions don't do what you expected them to do\. -.IP "\fB--accessor-regex\fR \fIregex\fR" -Add \fIregex\fR to the list of regular expressions used to transform data -member names to function names when searching for a suitable accessor -function\. The argument to this option is a Perl-like regular expression in -the form \fB/\fR\fIpattern\fR\fB/\fR\fIreplacement\fR\fB/\fR\fR\. Any -character can be used as a delimiter instead of '\fB/\fR' and the delimiter -can be escaped inside \fIpattern\fR and \fIreplacement\fR with a backslash -(\fB\e\fR)\. You can specify multiple regular expressions by repeating this -option\. - -All the regular expressions are tried in the order specified and the first -expression that produces a suitable accessor function is used\. Each -expression is tried twice: first with the actual member name and then with the -member's \fIpublic name\fR which is obtained by removing the common member -name decorations, such as leading and trailing underscores, the \fBm_\fR -prefix, etc\. The ODB compiler also includes a number of built-in expressions -for commonly used accessor names, such as \fBget_foo\fR, \fBgetFoo\fR, -\fBgetfoo\fR, and just \fBfoo\fR\. The built-in expressions are tried last\. - -As an example, the following expression transforms data members with public -names in the form \fBfoo\fR to accessor names in the form \fBGetFoo\fR: - -\fB/(\.+)/Get\eu$1/\fR - -See also the REGEX AND SHELL QUOTING section below\. -.IP "\fB--accessor-regex-trace\fR" -Trace the process of applying regular expressions specified with the -\fB--accessor-regex\fR option\. Use this option to find out why your regular -expressions don't do what you expected them to do\. -.IP "\fB--modifier-regex\fR \fIregex\fR" -Add \fIregex\fR to the list of regular expressions used to transform data -member names to function names when searching for a suitable modifier -function\. The argument to this option is a Perl-like regular expression in -the form \fB/\fR\fIpattern\fR\fB/\fR\fIreplacement\fR\fB/\fR\fR\. Any -character can be used as a delimiter instead of '\fB/\fR' and the delimiter -can be escaped inside \fIpattern\fR and \fIreplacement\fR with a backslash -(\fB\e\fR)\. You can specify multiple regular expressions by repeating this -option\. - -All the regular expressions are tried in the order specified and the first -expression that produces a suitable modifier function is used\. Each -expression is tried twice: first with the actual member name and then with the -member's \fIpublic name\fR which is obtained by removing the common member -name decorations, such as leading and trailing underscores, the \fBm_\fR -prefix, etc\. The ODB compiler also includes a number of built-in expressions -for commonly used modifier names, such as \fBset_foo\fR, \fBsetFoo\fR, -\fBsetfoo\fR, and just \fBfoo\fR\. The built-in expressions are tried last\. - -As an example, the following expression transforms data members with public -names in the form \fBfoo\fR to modifier names in the form \fBSetFoo\fR: - -\fB/(\.+)/Set\eu$1/\fR - -See also the REGEX AND SHELL QUOTING section below\. -.IP "\fB--modifier-regex-trace\fR" -Trace the process of applying regular expressions specified with the -\fB--modifier-regex\fR option\. Use this option to find out why your regular -expressions don't do what you expected them to do\. -.IP "\fB--include-with-brackets\fR" -Use angle brackets (<>) instead of quotes ("") in the generated \fB#include\fR -directives\. -.IP "\fB--include-prefix\fR \fIprefix\fR" -Add \fIprefix\fR to the generated \fB#include\fR directive paths\. -.IP "\fB--include-regex\fR \fIregex\fR" -Add \fIregex\fR to the list of regular expressions used to transform generated -\fB#include\fR directive paths\. The argument to this option is a Perl-like -regular expression in the form -\fB/\fR\fIpattern\fR\fB/\fR\fIreplacement\fR\fB/\fR\fR\. Any character can be -used as a delimiter instead of '\fB/\fR' and the delimiter can be escaped -inside \fIpattern\fR and \fIreplacement\fR with a backslash (\fB\e\fR)\. You -can specify multiple regular expressions by repeating this option\. All the -regular expressions are tried in the order specified and the first expression -that matches is used\. - -As an example, the following expression transforms include paths in the form -\fBfoo/bar-odb\.h\fR to paths in the form \fBfoo/generated/bar-odb\.h\fR: - -\fB%foo/(\.+)-odb\.h%foo/generated/$1-odb\.h%\fR - -See also the REGEX AND SHELL QUOTING section below\. -.IP "\fB--include-regex-trace\fR" -Trace the process of applying regular expressions specified with the -\fB--include-regex\fR option\. Use this option to find out why your regular -expressions don't do what you expected them to do\. -.IP "\fB--guard-prefix\fR \fIprefix\fR" -Add \fIprefix\fR to the generated header inclusion guards\. The prefix is -transformed to upper case and characters that are illegal in a preprocessor -macro name are replaced with underscores\. -.IP "\fB--show-sloc\fR" -Print the number of generated physical source lines of code (SLOC)\. -.IP "\fB--sloc-limit\fR \fInum\fR" -Check that the number of generated physical source lines of code (SLOC) does -not exceed \fInum\fR\. -.IP "\fB--options-file\fR \fIfile\fR" -Read additional options from \fIfile\fR\. Each option should appear on a -separate line optionally followed by space or equal sign (\fB=\fR) and an -option value\. Empty lines and lines starting with \fB#\fR are ignored\. -Option values can be enclosed in double (\fB"\fR) or single (\fB'\fR) quotes -to preserve leading and trailing whitespaces as well as to specify empty -values\. If the value itself contains trailing or leading quotes, enclose it -with an extra pair of quotes, for example \fB'"x"'\fR\. Non-leading and -non-trailing quotes are interpreted as being part of the option value\. - -The semantics of providing options in a file is equivalent to providing the -same set of options in the same order on the command line at the point where -the \fB--options-file\fR option is specified except that the shell escaping -and quoting is not required\. Repeat this option to specify more than one -options file\. -.IP "\fB-x\fR \fIoption\fR" -Pass \fIoption\fR to the underlying C++ compiler (\fBg++\fR)\. The -\fIoption\fR value that doesn't start with '\fB-\fR' is considered the -\fBg++\fR executable name\. -.IP "\fB-v\fR" -Print the commands executed to run the stages of compilation\. -.IP "\fB--trace\fR" -Trace the compilation process\. -.IP "\fB--mysql-engine\fR \fIengine\fR" -Use \fIengine\fR instead of the default \fBInnoDB\fR in the generated database -schema file\. For more information on the storage engine options see the MySQL -documentation\. If you would like to use the database-default engine, pass -\fBdefault\fR as the value for this option\. -.IP "\fB--sqlite-override-null\fR" -Make all columns in the generated database schema allow \fBNULL\fR values\. -This is primarily useful in schema migration since SQLite does not support -dropping of columns\. By making all columns \fBNULL\fR we can later "delete" -them by setting their values to \fBNULL\fR\. Note that this option overrides -even the \fBnot_null\fR pragma\. -.IP "\fB--sqlite-lax-auto-id\fR" -Do not force monotonically increasing automatically-assigned object ids\. In -this mode the generated database schema omits the \fBAUTOINCREMENT\fR keyword -which results in faster object persistence but may lead to -automatically-assigned ids not being in a strictly ascending order\. Refer to -the SQLite documentation for details\. -.IP "\fB--pgsql-server-version\fR \fIver\fR" -Specify the minimum PostgreSQL server version with which the generated C++ -code and schema will be used\. This information is used to enable -version-specific optimizations and workarounds in the generated C++ code and -schema\. The version must be in the \fImajor\fR\fB\.\fR\fIminor\fR\fR form, -for example, \fB9\.1\fR\. If this option is not specified, then \fB7\.4\fR or -later is assumed\. -.IP "\fB--oracle-client-version\fR \fIver\fR" -Specify the minimum Oracle client library (OCI) version with which the -generated C++ code will be linked\. This information is used to enable -version-specific optimizations and workarounds in the generated C++ code\. The -version must be in the \fImajor\fR\fB\.\fR\fIminor\fR\fR form, for example, -\fB11\.2\fR\. If this option is not specified, then \fB10\.1\fR or later is -assumed\. -.IP "\fB--oracle-warn-truncation\fR" -Warn about SQL names that are longer than 30 characters and are therefore -truncated\. Note that during database schema generation -(\fB--generate-schema\fR) ODB detects when such truncations lead to name -conflicts and issues diagnostics even without this option specified\. -.IP "\fB--mssql-server-version\fR \fIver\fR" -Specify the minimum SQL Server server version with which the generated C++ -code and schema will be used\. This information is used to enable -version-specific optimizations and workarounds in the generated C++ code and -schema\. The version must be in the \fImajor\fR\fB\.\fR\fIminor\fR\fR form, -for example, \fB9\.0\fR (SQL Server 2005), \fB10\.5\fR (2008R2), or -\fB11\.0\fR (2012)\. If this option is not specified, then \fB10\.0\fR (SQL -Server 2008) or later is assumed\. -.IP "\fB--mssql-short-limit\fR \fIsize\fR" -Specify the short data size limit\. If a character, national character, or -binary data type has a maximum length (in bytes) less than or equal to this -limit, then it is treated as \fIshort data\fR, otherwise it is \fIlong -data\fR\. 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 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 -\fBSQLGetData()\fR/\fBSQLPutData()\fR ODBC functions\. While the long data -approach reduces the amount of memory used by the application, it may require -greater CPU resources\. The default short data limit is 1024 bytes\. 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\. -.\" -.\" SQL NAME TRANSFORMATIONS -.\" -.SH SQL NAME TRANSFORMATIONS -The ODB compiler provides a number of mechanisms for transforming -automatically-derived SQL names, such as tables, columns, etc., -to match a specific naming convention. At the higher level, we can -add a prefix to global names (tables and, for some databases, -indexes and/or foreign keys) with the -.B --table-prefix -option. Similarly, we can specify custom suffixes for automatically-derived -index -.RB ( --index-suffix ; -default is -.BR _i ), -foreign key -.RB ( --fkey-suffix ; -default is -.BR _fk ), -and sequence -.RB ( --sequence-suffix ; -default is -.BR _seq ) -names. Finally, we can also convert all the names to upper or lower -case with the -.B --sql-name-case -option (valid values are -.B upper -and -.BR lower ). - -At the lower level we can specify a set of regular expressions to -implement arbitrary transformations of the automatically-derived SQL -names. If we want a particular regular expression only to apply to -a specific name, for example, table or column, then we use one of the -.B --\fIkind\fB-regex -options, where -.I kind -can be -.BR table , -.BR column , -.BR index , -.BR fkey , -.BR sequence , -or -.BR statement . -On the other hand, if we want our regular expressions to apply to all SQL -names, then we use the -.B --sql-name-regex -option. - -The interaction between the higher and lower level transformations -is as follows. Prefixes and suffixes are added first. Then the -regular expression transformations are applied. Finally, if requested, -the name is converted to upper or lower case. Note also that all of -these transformations except for -.B --table-prefix -only apply to automatically-derived names. In other words, if a table, -column, etc., name was explicitly specified with a pragma, then it -is used as is, without applying any (except for the table prefix) -transformations. - -The value for the -.B --*-regex -options is a Perl-like regular expression in the form -.BI / pattern / replacement /\fR. -Any character can be used as a delimiter instead of -.B / -and the delimiter can be escaped inside -.I pattern -and -.I replacement -with a backslash -.RB ( \e ). -You can also specify multiple regular expressions by repeating these -options. - -All the regular expressions are tried in the order specified with the -name-specific expressions (for example, -.BR --table-regex) -tried first followed by the generic expressions -.RB ( --sql-name-regex ). -The first expression that matches is used. - -As an example, consider a regular expression that transforms a class -name in the form -.B CFoo -to a table name in the form -.BR FOO: - -.B --table-regex '/C(.+)/\eU$1/' - -As a more interesting example, consider the transformation of class -names that follow the upper camel case convention (for example, -.BR FooBar ) -to table names that follow the underscore-separated, all upper case -convention (for example, -.BR FOO_BAR ). -For this case we have to use separate expressions to handle one-word, -two-word, etc., names: - -.B --table-regex '/([A-z][a-z]+)/\eU$1/' - -.B --table-regex '/([A-z][a-z]+)([A-z][a-z]+)/\eU$1_$2/' - -See also the REGEX AND SHELL QUOTING section below. -.\" -.\" REGEX AND SHELL QUOTING -.\" -.SH REGEX AND SHELL QUOTING -When entering a regular expression argument in the shell command line -it is often necessary to use quoting (enclosing the argument in " " -or ' ') in order to prevent the shell from interpreting certain -characters, for example, spaces as argument separators and $ as -variable expansions. - -Unfortunately it is hard to achieve this in a manner that is portable -across POSIX shells, such as those found on GNU/Linux and UNIX, and -Windows shell. For example, if you use " " for quoting you will get -a wrong result with POSIX shells if your expression contains $. The -standard way of dealing with this on POSIX systems is to use ' ' -instead. Unfortunately, Windows shell does not remove ' ' from -arguments when they are passed to applications. As a result you may -have to use ' ' for POSIX and " " for Windows ($ is not treated as -a special character on Windows). - -Alternatively, you can save regular expression options into a file, -one option per line, and use this file with the -.B --options-file -option. With this approach you don't need to worry about shell quoting. -.\" -.\" DIAGNOSTICS -.\" -.SH DIAGNOSTICS -If the input file is not valid C++, -.B odb -will issue diagnostic messages to STDERR and exit with non-zero exit code. -.\" -.\" BUGS -.\" -.SH BUGS -Send bug reports to the odb-users@codesynthesis.com mailing list. -.\" -.\" COPYRIGHT -.\" -.SH COPYRIGHT -Copyright (c) 2009-2023 Code Synthesis Tools CC. - -Permission is granted to copy, distribute and/or modify this -document under the terms of the GNU Free Documentation License, -version 1.2; with no Invariant Sections, no Front-Cover Texts and -no Back-Cover Texts. Copy of the license can be obtained from -http://www.codesynthesis.com/licenses/fdl-1.3.txt diff --git a/doc/pregenerated/odb.xhtml b/doc/pregenerated/odb.xhtml deleted file mode 100644 index 0a9785c..0000000 --- a/doc/pregenerated/odb.xhtml +++ /dev/null @@ -1,978 +0,0 @@ -<!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>ODB 2.4.0 Compiler Command Line Manual</title> - - <meta name="copyright" content="© 2009-2023 Code Synthesis Tools CC"/> - <meta name="keywords" content="odb,object,relational,mapping,compiler,c++"/> - <meta name="description" content="ODB Compiler Command Line Manual"/> - - <link rel="stylesheet" type="text/css" href="default.css" /> - -<style type="text/css"> - - #synopsis { - list-style-type: none; - } - - #synopsis li { - padding-top : 0.0em; - padding-bottom : 0.0em; - } - - .options { - margin: 1em 0 1em 0; - } - - .options dt { - margin: 1em 0 0 0; - } - - .options dd { - margin: .1em 0 0 4.5em; - } - -</style> -</head> - -<body> -<div id="container"> - <div id="content"> - - <h1>NAME</h1> - - <p>odb - object-relational mapping (ORM) compiler for C++</p> - - <h1>SYNOPSIS</h1> - - <dl id="synopsis"> - <dt><code><b>odb</b> [<i>options</i>] <i>file</i> [<i>file</i>...]</code></dt> - </dl> - - <h1>DESCRIPTION</h1> - - <p>Given a set of C++ classes in a header file, <code><b>odb</b></code> - generates C++ code that allows you to persist, query, and update objects - of these classes in a relational database (RDBMS). The relational - database that the generated code should target is specified with the - required <code><b>--database</b></code> option (see below).</p> - - <p>For an input file in the form <code><b>name.hxx</b></code> (other - file extensions can be used instead of <code><b>.hxx</b></code>), - in the single-database mode (the default), the generated C++ files - by default have the following names: - <code><b>name-odb.hxx</b></code> (header file), - <code><b>name-odb.ixx</b></code> (inline file), and - <code><b>name-odb.cxx</b></code> (source file). - - Additionally, if the <code><b>--generate-schema</b></code> option is - specified and the <code><b>sql</b></code> schema format is requested (see - <code><b>--schema-format</b></code>), the <code><b>name.sql</b></code> - database schema file is generated. If the <code><b>separate</b></code> - schema format is requested, the database creation code is generated - into the separate <code><b>name-schema.cxx</b></code> file.</p> - - <p>In the multi-database mode (see the <code><b>--multi-database</b></code> - option below), the generated files corresponding to the - <code><b>common</b></code> database have the same names as in the - single-database mode. For other databases, the file names include - the database name: - <code><b>name-odb-</b><i>db</i><b>.hxx</b></code>, - <code><b>name-odb-</b><i>db</i><b>.ixx</b></code>, - <code><b>name-odb-</b><i>db</i><b>.cxx</b></code>, - <code><b>name-</b><i>db</i><b>.sql</b></code>, and - <code><b>name-schema-</b><i>db</i><b>.cxx</b></code> - (where <code><i>db</i></code> is the database name).</p> - - <h1>OPTIONS</h1> - <dl class="options"> - <dt><code><b>--help</b></code></dt> - <dd>Print usage information and exit.</dd> - - <dt><code><b>--version</b></code></dt> - <dd>Print version and exit.</dd> - - <dt><code><b>-I</b></code> <code><i>dir</i></code></dt> - <dd>Add <code><i>dir</i></code> to the beginning of the list of - directories to be searched for included header files.</dd> - - <dt><code><b>-D</b></code> <code><i>name</i></code>[=<code><i>def</i></code>]</dt> - <dd>Define macro <code><i>name</i></code> with definition - <code><i>def</i></code>. If definition is omitted, define - <code><i>name</i></code> to be 1.</dd> - - <dt><code><b>-U</b></code> <code><i>name</i></code></dt> - <dd>Cancel any previous definitions of macro <code><i>name</i></code>, - either built-in or provided with the <code><b>-D</b></code> option.</dd> - - <dt><code><b>--database</b></code>|<code><b>-d</b></code> <code><i>db</i></code></dt> - <dd>Generate code for the <code><i>db</i></code> database. Valid values - are <code><b>mssql</b></code>, <code><b>mysql</b></code>, - <code><b>oracle</b></code>, <code><b>pgsql</b></code>, - <code><b>sqlite</b></code>, and <code><b>common</b></code> (multi-database - mode only).</dd> - - <dt><code><b>--multi-database</b></code>|<code><b>-m</b></code> <code><i>type</i></code></dt> - <dd>Enable multi-database support and specify its type. Valid values for - this option are <code><b>static</b></code> and - <code><b>dynamic</b></code>. - - <p>In the multi-database mode, options that determine the kind (for - example, <code><b>--schema-format</b></code>), names (for example, - <code><b>--odb-file-suffix</b></code>), or content (for example, prologue - and epilogue options) of the output files can be prefixed with the - database name followed by a colon, for example, - <code><b>mysql:value</b></code>. This restricts the value of such an - option to only apply to generated files corresponding to this - database.</p></dd> - - <dt><code><b>--default-database</b></code> <code><i>db</i></code></dt> - <dd>When static multi-database support is used, specify the database that - should be made the default. When dynamic multi-database support is used, - <code><b>common</b></code> is always made the default database.</dd> - - <dt><code><b>--generate-query</b></code>|<code><b>-q</b></code></dt> - <dd>Generate query support code. Without this support you cannot use views - and can only load objects via their ids.</dd> - - <dt><code><b>--generate-prepared</b></code></dt> - <dd>Generate prepared query execution support code.</dd> - - <dt><code><b>--omit-unprepared</b></code></dt> - <dd>Omit un-prepared (once-off) query execution support code.</dd> - - <dt><code><b>--generate-session</b></code>|<code><b>-e</b></code></dt> - <dd>Generate session support code. With this option session support will - be enabled by default for all the persistent classes except those for - which it was explicitly disabled using the <code><b>db session</b></code> - pragma.</dd> - - <dt><code><b>--generate-schema</b></code>|<code><b>-s</b></code></dt> - <dd>Generate the database schema. The database schema contains SQL - statements that create database tables necessary to store persistent - classes defined in the file being compiled. Note that by applying this - schema, all the existing information stored in such tables will be lost. - - <p>Depending on the database being used (<code><b>--database</b></code> - option), the schema is generated either as a standalone SQL file or - embedded into the generated C++ code. By default the SQL file is generated - for the MySQL, PostgreSQL, Oracle, and Microsoft SQL Server databases and - the schema is embedded into the C++ code for the SQLite database. Use the - <code><b>--schema-format</b></code> option to alter the default schema - format.</p> - - <p>If database schema evolution support is enabled (that is, the object - model version is specified), then this option also triggers the generation - of database schema migration statements, again either as standalong SQL - files or embedded into the generated C++ code. You can suppress the - generation of schema migration statements by specifying the - <code><b>--suppress-migration</b></code> option.</p></dd> - - <dt><code><b>--generate-schema-only</b></code></dt> - <dd>Generate only the database schema. Note that this option is only valid - when generating schema as a standalone SQL file (see - <code><b>--schema-format</b></code> for details).</dd> - - <dt><code><b>--suppress-migration</b></code></dt> - <dd>Suppress the generation of database schema migration statements.</dd> - - <dt><code><b>--suppress-schema-version</b></code></dt> - <dd>Suppress the generation of schema version table. If you specify this - option then you are also expected to manually specify the database schema - version and migration state at runtime using the - <code><b>odb::database::schema_version()</b></code> function.</dd> - - <dt><code><b>--schema-version-table</b></code> <code><i>name</i></code></dt> - <dd>Specify the alternative schema version table name instead of the - default <code><b>schema_version</b></code>. If you specify this option - then you are also expected to manually specify the schema version table - name at runtime using the - <code><b>odb::database::schema_version_table()</b></code> function. The - table name can be qualified.</dd> - - <dt><code><b>--schema-format</b></code> <code><i>format</i></code></dt> - <dd>Generate the database schema in the specified format. Pass - <code><b>sql</b></code> as <code><i>format</i></code> to generate the - database schema as a standalone SQL file or pass - <code><b>embedded</b></code> to embed the schema into the generated C++ - code. The <code><b>separate</b></code> value is similar to - <code><b>embedded</b></code> except the schema creation code is generated - into a separate C++ file (<code><b>name-schema.cxx</b></code> by default). - This value is primarily useful if you want to place the schema creation - functionality into a separate program or library. Repeat this option to - generate the same database schema in multiple formats.</dd> - - <dt><code><b>--omit-drop</b></code></dt> - <dd>Omit <code><b>DROP</b></code> statements from the generated database - schema.</dd> - - <dt><code><b>--omit-create</b></code></dt> - <dd>Omit <code><b>CREATE</b></code> statements from the generated database - schema.</dd> - - <dt><code><b>--schema-name</b></code> <code><i>name</i></code></dt> - <dd>Use <code><i>name</i></code> as the database schema name. Schema names - are primarily used to distinguish between multiple embedded schemas in the - schema catalog. They are not to be confused with database schemas - (database namespaces) which are specified with the - <code><b>--schema</b></code> option. If this option is not specified, the - empty name, which is the default schema name, is used.</dd> - - <dt><code><b>--fkeys-deferrable-mode</b></code> <code><i>m</i></code></dt> - <dd>Use constraint checking mode <code><i>m</i></code> in foreign keys - generated for object relationships. Valid values for this option are - <code><b>not_deferrable</b></code>, <code><b>immediate</b></code>, and - <code><b>deferred</b></code> (default). MySQL and SQL Server do not - support deferrable foreign keys and for these databases such keys are - generated commented out. Other foreign keys generated by the ODB compiler - (such as the ones used to support containers and polymorphic hierarchies) - are always generated as not deferrable. - - <p>Note also that if you use either <code><b>not_deferrable</b></code> or - <code><b>immediate</b></code> mode, then the order in which you persist, - update, and erase objects within a transaction becomes important.</p></dd> - - <dt><code><b>--default-pointer</b></code> <code><i>ptr</i></code></dt> - <dd>Use <code><i>ptr</i></code> as the default pointer for persistent - objects and views. Objects and views that do not have a pointer assigned - with the <code><b>db pointer</b></code> pragma will use this pointer by - default. The value of this option can be '<code><b>*</b></code>' which - denotes the raw pointer and is the default, or qualified name of a smart - pointer class template, for example, <code><b>std::shared_ptr</b></code>. - In the latter case, the ODB compiler constructs the object or view pointer - by adding a single template argument of the object or view type to the - qualified name, for example - <code><b>std::shared_ptr<object></b></code>. The ODB runtime uses - object and view pointers to return, and, in case of objects, pass and - cache dynamically allocated instances of object and view types. - - <p>Except for the raw pointer and the standard smart pointers defined in - the <code><b><memory></b></code> header file, you are expected to - include the definition of the default pointer at the beginning of the - generated header file. There are two common ways to achieve this: you can - either include the necessary header in the file being compiled or you can - use the <code><b>--hxx-prologue</b></code> option to add the necessary - <code><b>#include</b></code> directive to the generated code.</p></dd> - - <dt><code><b>--session-type</b></code> <code><i>type</i></code></dt> - <dd>Use <code><i>type</i></code> as the alternative session type instead - of the default <code><b>odb::session</b></code>. This option can be used - to specify a custom session implementation to be use by the persistent - classes. Note that you will also need to include the definition of the - custom session type into the generated header file. This is normally - achieved with the <code><b>--hxx-prologue*</b></code> options.</dd> - - <dt><code><b>--profile</b></code>|<code><b>-p</b></code> <code><i>name</i></code></dt> - <dd>Specify a profile that should be used during compilation. A profile is - an options file. The ODB compiler first looks for a database-specific - version with the name constructed by appending the - <code><b>-</b></code><code><i>database</i></code><code><b>.options</b></code> - suffix to <code><i>name</i></code>, where <code><i>database</i></code> is - the database name as specified with the <code><b>--database</b></code> - option. If this file is not found, then the ODB compiler looks for a - database-independant version with the name constructed by appending just - the <code><b>.options</b></code> suffix. - - <p>The profile options files are searched for in the same set of - directories as C++ headers included with the <code><b>#include - <...></b></code> directive (built-in paths plus those specified with - the <code><b>-I</b></code> options). The options file is first searched - for in the directory itself and then in its <code><b>odb/</b></code> - subdirectory.</p> - - <p>For the format of the options file refer to the - <code><b>--options-file</b></code> option below. You can repeat this - option to specify more than one profile.</p></dd> - - <dt><code><b>--at-once</b></code></dt> - <dd>Generate code for all the input files as well as for all the files - that they include at once. The result is a single set of source/schema - files that contain all the generated code. If more than one input file is - specified together with this option, then the - <code><b>--input-name</b></code> option must also be specified in order to - provide the base name for the output files. In this case, the directory - part of such a base name is used as the location of the combined file. - This can be important for the <code><b>#include</b></code> directive - resolution.</dd> - - <dt><code><b>--schema</b></code> <code><i>schema</i></code></dt> - <dd>Specify a database schema (database namespace) that should be assigned - to the persistent classes in the file being compiled. Database schemas are - not to be confused with database schema names (schema catalog names) which - are specified with the <code><b>--schema-name</b></code> option.</dd> - - <dt><code><b>--export-symbol</b></code> <code><i>symbol</i></code></dt> - <dd>Insert <code><i>symbol</i></code> in places where DLL export/import - control statements (<code><b>__declspec(dllexport/dllimport)</b></code>) - are necessary. See also the <code><b>--extern-symbol</b></code> option - below.</dd> - - <dt><code><b>--extern-symbol</b></code> <code><i>symbol</i></code></dt> - <dd>If <code><i>symbol</i></code> is defined, insert it in places where a - template instantiation must be declared <code><b>extern</b></code>. This - option is normally used together with <code><b>--export-symbol</b></code> - when both multi-database support and queries are enabled.</dd> - - <dt><code><b>--std</b></code> <code><i>version</i></code></dt> - <dd>Specify the C++ standard that should be used during compilation. Valid - values are <code><b>c++98</b></code> (default), <code><b>c++11</b></code>, - <code><b>c++14</b></code>, <code><b>c++17</b></code>, and - <code><b>c++20</b></code>.</dd> - - <dt><code><b>--warn-hard-add</b></code></dt> - <dd>Warn about hard-added data members.</dd> - - <dt><code><b>--warn-hard-delete</b></code></dt> - <dd>Warn about hard-deleted data members and persistent classes.</dd> - - <dt><code><b>--warn-hard</b></code></dt> - <dd>Warn about both hard-added and hard-deleted data members and - persistent classes.</dd> - - <dt><code><b>--output-dir</b></code>|<code><b>-o</b></code> <code><i>dir</i></code></dt> - <dd>Write the generated files to <code><i>dir</i></code> instead of the - current directory.</dd> - - <dt><code><b>--input-name</b></code> <code><i>name</i></code></dt> - <dd>Use <code><i>name</i></code> instead of the input file to derive the - names of the generated files. If the <code><b>--at-once</b></code> option - is specified, then the directory part of <code><i>name</i></code> is used - as the location of the combined file. Refer to the - <code><b>--at-once</b></code> option for details.</dd> - - <dt><code><b>--changelog</b></code> <code><i>file</i></code></dt> - <dd>Read/write changelog from/to <code><i>file</i></code> instead of the - default changelog file. The default changelog file name is derived from - the input file name and it is placed into the same directory as the input - file. Note that the <code><b>--output-dir</b></code> option does not - affect the changelog file location. In other words, by default, the - changelog file is treated as another input rather than output even though - the ODB compiler may modify it. Use the <code><b>--changelog-in</b></code> - and <code><b>--changelog-out</b></code> options to specify different input - and output chaneglog files.</dd> - - <dt><code><b>--changelog-in</b></code> <code><i>file</i></code></dt> - <dd>Read changelog from <code><i>file</i></code> instead of the default - changelog file. If this option is specified, then you must also specify - the output chanegelog file with <code><b>--changelog-out</b></code>.</dd> - - <dt><code><b>--changelog-out</b></code> <code><i>file</i></code></dt> - <dd>Write changelog to <code><i>file</i></code> instead of the default - changelog file. If this option is specified, then you must also specify - the input chanegelog file with <code><b>--changelog-in</b></code>.</dd> - - <dt><code><b>--changelog-dir</b></code> <code><i>dir</i></code></dt> - <dd>Use <code><i>dir</i></code> instead of the input file directory as the - changelog file directory. This directory is also added to changelog files - specified with the <code><b>--changelog</b></code>, - <code><b>--changelog-in</b></code>, and <code><b>--changelog-in</b></code> - options unless they are absolute paths.</dd> - - <dt><code><b>--init-changelog</b></code></dt> - <dd>Force re-initialization of the changelog even if one exists (all the - existing change history will be lost). This option is primarily useful for - automated testing.</dd> - - <dt><code><b>--odb-file-suffix</b></code> <code><i>suffix</i></code></dt> - <dd>Use <code><i>suffix</i></code> to construct the names of the generated - C++ files. In the single-database mode the default value for this option - is <code><b>-odb</b></code>. In the multi-database mode it is - <code><b>-odb</b></code> for the files corresponding to the - <code><b>common</b></code> database and <code><b>-odb-</b><i>db</i></code> - (where <code><i>db</i></code> is the database name) for other - databases.</dd> - - <dt><code><b>--sql-file-suffix</b></code> <code><i>suffix</i></code></dt> - <dd>Use <code><i>suffix</i></code> to construct the name of the generated - schema SQL file. In the single-database mode by default no suffix is used. - In the multi-database mode the default value for this option is - <code><b>-</b><i>db</i></code> (where <code><i>db</i></code> is the - database name).</dd> - - <dt><code><b>--schema-file-suffix</b></code> <code><i>suffix</i></code></dt> - <dd>Use <code><i>suffix</i></code> to construct the name of the generated - schema C++ source file. In the single-database mode the default value for - this option is <code><b>-schema</b></code>. In the multi-database mode it - is <code><b>-schema-</b><i>db</i></code> (where <code><i>db</i></code> is - the database name). See the <code><b>--schema-format</b></code> option for - details.</dd> - - <dt><code><b>--changelog-file-suffix</b></code> <code><i>sfx</i></code></dt> - <dd>Use <code><i>sfx</i></code> to construct the name of the changelog - file. In the single-database mode by default no suffix is used. In the - multi-database mode the default value for this option is - <code><b>-</b><i>db</i></code> (where <code><i>db</i></code> is the - database name).</dd> - - <dt><code><b>--hxx-suffix</b></code> <code><i>suffix</i></code></dt> - <dd>Use <code><i>suffix</i></code> instead of the default - <code><b>.hxx</b></code> to construct the name of the generated C++ header - file.</dd> - - <dt><code><b>--ixx-suffix</b></code> <code><i>suffix</i></code></dt> - <dd>Use <code><i>suffix</i></code> instead of the default - <code><b>.ixx</b></code> to construct the name of the generated C++ inline - file.</dd> - - <dt><code><b>--cxx-suffix</b></code> <code><i>suffix</i></code></dt> - <dd>Use <code><i>suffix</i></code> instead of the default - <code><b>.cxx</b></code> to construct the name of the generated C++ source - file.</dd> - - <dt><code><b>--sql-suffix</b></code> <code><i>suffix</i></code></dt> - <dd>Use <code><i>suffix</i></code> instead of the default - <code><b>.sql</b></code> to construct the name of the generated database - schema file.</dd> - - <dt><code><b>--changelog-suffix</b></code> <code><i>suffix</i></code></dt> - <dd>Use <code><i>suffix</i></code> instead of the default - <code><b>.xml</b></code> to construct the name of the changelog file.</dd> - - <dt><code><b>--hxx-prologue</b></code> <code><i>text</i></code></dt> - <dd>Insert <code><i>text</i></code> at the beginning of the generated C++ - header file.</dd> - - <dt><code><b>--ixx-prologue</b></code> <code><i>text</i></code></dt> - <dd>Insert <code><i>text</i></code> at the beginning of the generated C++ - inline file.</dd> - - <dt><code><b>--cxx-prologue</b></code> <code><i>text</i></code></dt> - <dd>Insert <code><i>text</i></code> at the beginning of the generated C++ - source file.</dd> - - <dt><code><b>--schema-prologue</b></code> <code><i>text</i></code></dt> - <dd>Insert <code><i>text</i></code> at the beginning of the generated - schema C++ source file.</dd> - - <dt><code><b>--sql-prologue</b></code> <code><i>text</i></code></dt> - <dd>Insert <code><i>text</i></code> at the beginning of the generated - database schema file.</dd> - - <dt><code><b>--migration-prologue</b></code> <code><i>text</i></code></dt> - <dd>Insert <code><i>text</i></code> at the beginning of the generated - database migration file.</dd> - - <dt><code><b>--sql-interlude</b></code> <code><i>text</i></code></dt> - <dd>Insert <code><i>text</i></code> after all the <code><b>DROP</b></code> - and before any <code><b>CREATE</b></code> statements in the generated - database schema file.</dd> - - <dt><code><b>--hxx-epilogue</b></code> <code><i>text</i></code></dt> - <dd>Insert <code><i>text</i></code> at the end of the generated C++ header - file.</dd> - - <dt><code><b>--ixx-epilogue</b></code> <code><i>text</i></code></dt> - <dd>Insert <code><i>text</i></code> at the end of the generated C++ inline - file.</dd> - - <dt><code><b>--cxx-epilogue</b></code> <code><i>text</i></code></dt> - <dd>Insert <code><i>text</i></code> at the end of the generated C++ source - file.</dd> - - <dt><code><b>--schema-epilogue</b></code> <code><i>text</i></code></dt> - <dd>Insert <code><i>text</i></code> at the end of the generated schema C++ - source file.</dd> - - <dt><code><b>--sql-epilogue</b></code> <code><i>text</i></code></dt> - <dd>Insert <code><i>text</i></code> at the end of the generated database - schema file.</dd> - - <dt><code><b>--migration-epilogue</b></code> <code><i>text</i></code></dt> - <dd>Insert <code><i>text</i></code> at the end of the generated database - migration file.</dd> - - <dt><code><b>--hxx-prologue-file</b></code> <code><i>file</i></code></dt> - <dd>Insert the content of <code><i>file</i></code> at the beginning of the - generated C++ header file.</dd> - - <dt><code><b>--ixx-prologue-file</b></code> <code><i>file</i></code></dt> - <dd>Insert the content of <code><i>file</i></code> at the beginning of the - generated C++ inline file.</dd> - - <dt><code><b>--cxx-prologue-file</b></code> <code><i>file</i></code></dt> - <dd>Insert the content of <code><i>file</i></code> at the beginning of the - generated C++ source file.</dd> - - <dt><code><b>--schema-prologue-file</b></code> <code><i>file</i></code></dt> - <dd>Insert the content of <code><i>file</i></code> at the beginning of the - generated schema C++ source file.</dd> - - <dt><code><b>--sql-prologue-file</b></code> <code><i>file</i></code></dt> - <dd>Insert the content of <code><i>file</i></code> at the beginning of the - generated database schema file.</dd> - - <dt><code><b>--migration-prologue-file</b></code> <code><i>f</i></code></dt> - <dd>Insert the content of file <code><i>f</i></code> at the beginning of - the generated database migration file.</dd> - - <dt><code><b>--sql-interlude-file</b></code> <code><i>file</i></code></dt> - <dd>Insert the content of <code><i>file</i></code> after all the - <code><b>DROP</b></code> and before any <code><b>CREATE</b></code> - statements in the generated database schema file.</dd> - - <dt><code><b>--hxx-epilogue-file</b></code> <code><i>file</i></code></dt> - <dd>Insert the content of <code><i>file</i></code> at the end of the - generated C++ header file.</dd> - - <dt><code><b>--ixx-epilogue-file</b></code> <code><i>file</i></code></dt> - <dd>Insert the content of <code><i>file</i></code> at the end of the - generated C++ inline file.</dd> - - <dt><code><b>--cxx-epilogue-file</b></code> <code><i>file</i></code></dt> - <dd>Insert the content of <code><i>file</i></code> at the end of the - generated C++ source file.</dd> - - <dt><code><b>--schema-epilogue-file</b></code> <code><i>file</i></code></dt> - <dd>Insert the content of <code><i>file</i></code> at the end of the - generated schema C++ source file.</dd> - - <dt><code><b>--sql-epilogue-file</b></code> <code><i>file</i></code></dt> - <dd>Insert the content of <code><i>file</i></code> at the end of the - generated database schema file.</dd> - - <dt><code><b>--migration-epilogue-file</b></code> <code><i>f</i></code></dt> - <dd>Insert the content of file <code><i>f</i></code> at the end of the - generated database migration file.</dd> - - <dt><code><b>--odb-prologue</b></code> <code><i>text</i></code></dt> - <dd>Compile <code><i>text</i></code> before the input header file. This - option allows you to add additional declarations, such as custom traits - specializations, to the ODB compilation process.</dd> - - <dt><code><b>--odb-prologue-file</b></code> <code><i>file</i></code></dt> - <dd>Compile <code><i>file</i></code> contents before the input header - file. Prologue files are compiled after all the prologue text fragments - (<code><b>--odb-prologue</b></code> option).</dd> - - <dt><code><b>--odb-epilogue</b></code> <code><i>text</i></code></dt> - <dd>Compile <code><i>text</i></code> after the input header file. This - option allows you to add additional declarations, such as custom traits - specializations, to the ODB compilation process.</dd> - - <dt><code><b>--odb-epilogue-file</b></code> <code><i>file</i></code></dt> - <dd>Compile <code><i>file</i></code> contents after the input header file. - Epilogue files are compiled after all the epilogue text fragments - (<code><b>--odb-epilogue</b></code> option).</dd> - - <dt><code><b>--table-prefix</b></code> <code><i>prefix</i></code></dt> - <dd>Add <code><i>prefix</i></code> to table names and, for databases that - have global index and/or foreign key names, to those names as well. The - prefix is added to both names that were specified with the <code><b>db - table</b></code> and <code><b>db index</b></code> pragmas and those that - were automatically derived from class and data member names. If you - require a separator, such as an underscore, between the prefix and the - name, then you should include it into the prefix value.</dd> - - <dt><code><b>--index-suffix</b></code> <code><i>suffix</i></code></dt> - <dd>Use <code><i>suffix</i></code> instead of the default - <code><b>_i</b></code> to construct index names. The suffix is only added - to names that were automatically derived from data member names. If you - require a separator, such as an underscore, between the name and the - suffix, then you should include it into the suffix value.</dd> - - <dt><code><b>--fkey-suffix</b></code> <code><i>suffix</i></code></dt> - <dd>Use <code><i>suffix</i></code> instead of the default - <code><b>_fk</b></code> to construct foreign key names. If you require a - separator, such as an underscore, between the name and the suffix, then - you should include it into the suffix value.</dd> - - <dt><code><b>--sequence-suffix</b></code> <code><i>suffix</i></code></dt> - <dd>Use <code><i>suffix</i></code> instead of the default - <code><b>_seq</b></code> to construct sequence names. If you require a - separator, such as an underscore, between the name and the suffix, then - you should include it into the suffix value.</dd> - - <dt><code><b>--sql-name-case</b></code> <code><i>case</i></code></dt> - <dd>Convert all automatically-derived SQL names to upper or lower case. - Valid values for this option are <code><b>upper</b></code> and - <code><b>lower</b></code>.</dd> - - <dt><code><b>--table-regex</b></code> <code><i>regex</i></code></dt> - <dd>Add <code><i>regex</i></code> to the list of regular expressions that - is used to transform automatically-derived table names. See the SQL NAME - TRANSFORMATIONS section below for details.</dd> - - <dt><code><b>--column-regex</b></code> <code><i>regex</i></code></dt> - <dd>Add <code><i>regex</i></code> to the list of regular expressions that - is used to transform automatically-derived column names. See the SQL NAME - TRANSFORMATIONS section below for details.</dd> - - <dt><code><b>--index-regex</b></code> <code><i>regex</i></code></dt> - <dd>Add <code><i>regex</i></code> to the list of regular expressions that - is used to transform automatically-derived index names. See the SQL NAME - TRANSFORMATIONS section below for details.</dd> - - <dt><code><b>--fkey-regex</b></code> <code><i>regex</i></code></dt> - <dd>Add <code><i>regex</i></code> to the list of regular expressions that - is used to transform automatically-derived foreign key names. See the SQL - NAME TRANSFORMATIONS section below for details.</dd> - - <dt><code><b>--sequence-regex</b></code> <code><i>regex</i></code></dt> - <dd>Add <code><i>regex</i></code> to the list of regular expressions that - is used to transform automatically-derived sequence names. See the SQL - NAME TRANSFORMATIONS section below for details.</dd> - - <dt><code><b>--statement-regex</b></code> <code><i>regex</i></code></dt> - <dd>Add <code><i>regex</i></code> to the list of regular expressions that - is used to transform automatically-derived prepared statement names. See - the SQL NAME TRANSFORMATIONS section below for details.</dd> - - <dt><code><b>--sql-name-regex</b></code> <code><i>regex</i></code></dt> - <dd>Add <code><i>regex</i></code> to the list of regular expressions that - is used to transform all automatically-derived SQL names. See the SQL NAME - TRANSFORMATIONS section below for details.</dd> - - <dt><code><b>--sql-name-regex-trace</b></code></dt> - <dd>Trace the process of applying regular expressions specified with the - SQL name <code><b>--*-regex</b></code> options. Use this option to find - out why your regular expressions don't do what you expected them to - do.</dd> - - <dt><code><b>--accessor-regex</b></code> <code><i>regex</i></code></dt> - <dd>Add <code><i>regex</i></code> to the list of regular expressions used - to transform data member names to function names when searching for a - suitable accessor function. The argument to this option is a Perl-like - regular expression in the form - <code><b>/</b><i>pattern</i><b>/</b><i>replacement</i><b>/</b></code>. Any - character can be used as a delimiter instead of '<code><b>/</b></code>' - and the delimiter can be escaped inside <code><i>pattern</i></code> and - <code><i>replacement</i></code> with a backslash (<code><b>\</b></code>). - You can specify multiple regular expressions by repeating this option. - - <p>All the regular expressions are tried in the order specified and the - first expression that produces a suitable accessor function is used. Each - expression is tried twice: first with the actual member name and then with - the member's <i>public name</i> which is obtained by removing the common - member name decorations, such as leading and trailing underscores, the - <code><b>m_</b></code> prefix, etc. The ODB compiler also includes a - number of built-in expressions for commonly used accessor names, such as - <code><b>get_foo</b></code>, <code><b>getFoo</b></code>, - <code><b>getfoo</b></code>, and just <code><b>foo</b></code>. The built-in - expressions are tried last.</p> - - <p>As an example, the following expression transforms data members with - public names in the form <code><b>foo</b></code> to accessor names in the - form <code><b>GetFoo</b></code>:</p> - - <p class="code"><code><b>/(.+)/Get\u$1/</b></code></p> - - <p>See also the REGEX AND SHELL QUOTING section below.</p></dd> - - <dt><code><b>--accessor-regex-trace</b></code></dt> - <dd>Trace the process of applying regular expressions specified with the - <code><b>--accessor-regex</b></code> option. Use this option to find out - why your regular expressions don't do what you expected them to do.</dd> - - <dt><code><b>--modifier-regex</b></code> <code><i>regex</i></code></dt> - <dd>Add <code><i>regex</i></code> to the list of regular expressions used - to transform data member names to function names when searching for a - suitable modifier function. The argument to this option is a Perl-like - regular expression in the form - <code><b>/</b><i>pattern</i><b>/</b><i>replacement</i><b>/</b></code>. Any - character can be used as a delimiter instead of '<code><b>/</b></code>' - and the delimiter can be escaped inside <code><i>pattern</i></code> and - <code><i>replacement</i></code> with a backslash (<code><b>\</b></code>). - You can specify multiple regular expressions by repeating this option. - - <p>All the regular expressions are tried in the order specified and the - first expression that produces a suitable modifier function is used. Each - expression is tried twice: first with the actual member name and then with - the member's <i>public name</i> which is obtained by removing the common - member name decorations, such as leading and trailing underscores, the - <code><b>m_</b></code> prefix, etc. The ODB compiler also includes a - number of built-in expressions for commonly used modifier names, such as - <code><b>set_foo</b></code>, <code><b>setFoo</b></code>, - <code><b>setfoo</b></code>, and just <code><b>foo</b></code>. The built-in - expressions are tried last.</p> - - <p>As an example, the following expression transforms data members with - public names in the form <code><b>foo</b></code> to modifier names in the - form <code><b>SetFoo</b></code>:</p> - - <p class="code"><code><b>/(.+)/Set\u$1/</b></code></p> - - <p>See also the REGEX AND SHELL QUOTING section below.</p></dd> - - <dt><code><b>--modifier-regex-trace</b></code></dt> - <dd>Trace the process of applying regular expressions specified with the - <code><b>--modifier-regex</b></code> option. Use this option to find out - why your regular expressions don't do what you expected them to do.</dd> - - <dt><code><b>--include-with-brackets</b></code></dt> - <dd>Use angle brackets (<>) instead of quotes ("") in the generated - <code><b>#include</b></code> directives.</dd> - - <dt><code><b>--include-prefix</b></code> <code><i>prefix</i></code></dt> - <dd>Add <code><i>prefix</i></code> to the generated - <code><b>#include</b></code> directive paths.</dd> - - <dt><code><b>--include-regex</b></code> <code><i>regex</i></code></dt> - <dd>Add <code><i>regex</i></code> to the list of regular expressions used - to transform generated <code><b>#include</b></code> directive paths. The - argument to this option is a Perl-like regular expression in the form - <code><b>/</b><i>pattern</i><b>/</b><i>replacement</i><b>/</b></code>. Any - character can be used as a delimiter instead of '<code><b>/</b></code>' - and the delimiter can be escaped inside <code><i>pattern</i></code> and - <code><i>replacement</i></code> with a backslash (<code><b>\</b></code>). - You can specify multiple regular expressions by repeating this option. All - the regular expressions are tried in the order specified and the first - expression that matches is used. - - <p>As an example, the following expression transforms include paths in the - form <code><b>foo/bar-odb.h</b></code> to paths in the form - <code><b>foo/generated/bar-odb.h</b></code>:</p> - - <p - class="code"><code><b>%foo/(.+)-odb.h%foo/generated/$1-odb.h%</b></code></p> - - <p>See also the REGEX AND SHELL QUOTING section below.</p></dd> - - <dt><code><b>--include-regex-trace</b></code></dt> - <dd>Trace the process of applying regular expressions specified with the - <code><b>--include-regex</b></code> option. Use this option to find out - why your regular expressions don't do what you expected them to do.</dd> - - <dt><code><b>--guard-prefix</b></code> <code><i>prefix</i></code></dt> - <dd>Add <code><i>prefix</i></code> to the generated header inclusion - guards. The prefix is transformed to upper case and characters that are - illegal in a preprocessor macro name are replaced with underscores.</dd> - - <dt><code><b>--show-sloc</b></code></dt> - <dd>Print the number of generated physical source lines of code - (SLOC).</dd> - - <dt><code><b>--sloc-limit</b></code> <code><i>num</i></code></dt> - <dd>Check that the number of generated physical source lines of code - (SLOC) does not exceed <code><i>num</i></code>.</dd> - - <dt><code><b>--options-file</b></code> <code><i>file</i></code></dt> - <dd>Read additional options from <code><i>file</i></code>. Each option - should appear on a separate line optionally followed by space or equal - sign (<code><b>=</b></code>) and an option value. Empty lines and lines - starting with <code><b>#</b></code> are ignored. Option values can be - enclosed in double (<code><b>"</b></code>) or single - (<code><b>'</b></code>) quotes to preserve leading and trailing - whitespaces as well as to specify empty values. If the value itself - contains trailing or leading quotes, enclose it with an extra pair of - quotes, for example <code><b>'"x"'</b></code>. Non-leading and - non-trailing quotes are interpreted as being part of the option value. - - <p>The semantics of providing options in a file is equivalent to providing - the same set of options in the same order on the command line at the point - where the <code><b>--options-file</b></code> option is specified except - that the shell escaping and quoting is not required. Repeat this option to - specify more than one options file.</p></dd> - - <dt><code><b>-x</b></code> <code><i>option</i></code></dt> - <dd>Pass <code><i>option</i></code> to the underlying C++ compiler - (<code><b>g++</b></code>). The <code><i>option</i></code> value that - doesn't start with '<code><b>-</b></code>' is considered the - <code><b>g++</b></code> executable name.</dd> - - <dt><code><b>-v</b></code></dt> - <dd>Print the commands executed to run the stages of compilation.</dd> - - <dt><code><b>--trace</b></code></dt> - <dd>Trace the compilation process.</dd> - - <dt><code><b>--mysql-engine</b></code> <code><i>engine</i></code></dt> - <dd>Use <code><i>engine</i></code> instead of the default - <code><b>InnoDB</b></code> in the generated database schema file. For more - information on the storage engine options see the MySQL documentation. If - you would like to use the database-default engine, pass - <code><b>default</b></code> as the value for this option.</dd> - - <dt><code><b>--sqlite-override-null</b></code></dt> - <dd>Make all columns in the generated database schema allow - <code><b>NULL</b></code> values. This is primarily useful in schema - migration since SQLite does not support dropping of columns. By making all - columns <code><b>NULL</b></code> we can later "delete" them by setting - their values to <code><b>NULL</b></code>. Note that this option overrides - even the <code><b>not_null</b></code> pragma.</dd> - - <dt><code><b>--sqlite-lax-auto-id</b></code></dt> - <dd>Do not force monotonically increasing automatically-assigned object - ids. In this mode the generated database schema omits the - <code><b>AUTOINCREMENT</b></code> keyword which results in faster object - persistence but may lead to automatically-assigned ids not being in a - strictly ascending order. Refer to the SQLite documentation for - details.</dd> - - <dt><code><b>--pgsql-server-version</b></code> <code><i>ver</i></code></dt> - <dd>Specify the minimum PostgreSQL server version with which the generated - C++ code and schema will be used. This information is used to enable - version-specific optimizations and workarounds in the generated C++ code - and schema. The version must be in the - <code><i>major</i><b>.</b><i>minor</i></code> form, for example, - <code><b>9.1</b></code>. If this option is not specified, then - <code><b>7.4</b></code> or later is assumed.</dd> - - <dt><code><b>--oracle-client-version</b></code> <code><i>ver</i></code></dt> - <dd>Specify the minimum Oracle client library (OCI) version with which the - generated C++ code will be linked. This information is used to enable - version-specific optimizations and workarounds in the generated C++ code. - The version must be in the <code><i>major</i><b>.</b><i>minor</i></code> - form, for example, <code><b>11.2</b></code>. If this option is not - specified, then <code><b>10.1</b></code> or later is assumed.</dd> - - <dt><code><b>--oracle-warn-truncation</b></code></dt> - <dd>Warn about SQL names that are longer than 30 characters and are - therefore truncated. Note that during database schema generation - (<code><b>--generate-schema</b></code>) ODB detects when such truncations - lead to name conflicts and issues diagnostics even without this option - specified.</dd> - - <dt><code><b>--mssql-server-version</b></code> <code><i>ver</i></code></dt> - <dd>Specify the minimum SQL Server server version with which the generated - C++ code and schema will be used. This information is used to enable - version-specific optimizations and workarounds in the generated C++ code - and schema. The version must be in the - <code><i>major</i><b>.</b><i>minor</i></code> form, for example, - <code><b>9.0</b></code> (SQL Server 2005), <code><b>10.5</b></code> - (2008R2), or <code><b>11.0</b></code> (2012). If this option is not - specified, then <code><b>10.0</b></code> (SQL Server 2008) or later is - assumed.</dd> - - <dt><code><b>--mssql-short-limit</b></code> <code><i>size</i></code></dt> - <dd>Specify the short data size limit. If a character, national character, - or binary data type has a maximum length (in bytes) less than or equal to - this limit, then it is treated as <i>short data</i>, otherwise it is - <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 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><b>SQLGetData()</b></code>/<code><b>SQLPutData()</b></code> ODBC - functions. While the long data approach reduces the amount of memory used - by the application, it may require greater CPU resources. The default - short data limit is 1024 bytes. 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.</dd> - </dl> - - <h1>SQL NAME TRANSFORMATIONS</h1> - - <p>The ODB compiler provides a number of mechanisms for transforming - automatically-derived SQL names, such as tables, columns, etc., - to match a specific naming convention. At the higher level, we can - add a prefix to global names (tables and, for some databases, - indexes and/or foreign keys) with the <code><b>--table-prefix</b></code> - option. Similarly, we can specify custom suffixes for - automatically-derived - index (<code><b>--index-suffix</b></code>; default is <code><b>_i</b></code>), - foreign key (<code><b>--fkey-suffix</b></code>; default is <code><b>_fk</b></code>), and - sequence (<code><b>--sequence-suffix</b></code>; default is <code><b>_seq</b></code>) - names. Finally, we can also convert all the names to upper or lower - case with the <code><b>--sql-name-case</b></code> option (valid values - are <code><b>upper</b></code> and <code><b>lower</b></code>).</p> - - <p>At the lower level we can specify a set of regular expressions to - implement arbitrary transformations of the automatically-derived SQL - names. If we want a particular regular expression only to apply to - a specific name, for example, table or column, then we use one of the - <code><b>--</b><i>kind</i><b>-regex</b></code> options, where - <code><i>kind</i></code> can be <code><b>table</b></code>, - <code><b>column</b></code>, <code><b>index</b></code>, - <code><b>fkey</b></code>, <code><b>sequence</b></code>, or - <code><b>statement</b></code>. On the other hand, if we want our - regular expressions to apply to all SQL names, then we use the - <code><b>--sql-name-regex</b></code> option.</p> - - <p>The interaction between the higher and lower level transformations - is as follows. Prefixes and suffixes are added first. Then the - regular expression transformations are applied. Finally, if requested, - the name is converted to upper or lower case. Note also that all of - these transformations except for <code><b>--table-prefix</b></code> - only apply to automatically-derived names. In other words, if a table, - column, etc., name was explicitly specified with a pragma, then it - is used as is, without applying any (except for the table prefix) - transformations.</p> - - <p>The value for the <code><b>--*-regex</b></code> options is a Perl-like - regular expression in the form - <code><b>/</b><i>pattern</i><b>/</b><i>replacement</i><b>/</b></code>. - Any character can be used as a delimiter instead of <code><b>/</b></code> - and the delimiter can be escaped inside <code><i>pattern</i></code> and - <code><i>replacement</i></code> with a backslash (<code><b>\</b></code>). - You can also specify multiple regular expressions by repeating these - options.</p> - - <p>All the regular expressions are tried in the order specified with the - name-specific expressions (for example, <code><b>--table-regex</b></code>) - tried first followed by the generic expressions - (<code><b>--sql-name-regex</b></code>). The first expression that - matches is used.</p> - - <p>As an example, consider a regular expression that transforms a class - name in the form <code><b>CFoo</b></code> to a table name in the - form <code><b>FOO</b></code>:</p> - - <p><code><b>--table-regex '/C(.+)/\U$1/'</b></code></p> - - <p>As a more interesting example, consider the transformation of class - names that follow the upper camel case convention (for example, - <code><b>FooBar</b></code>) to table names that follow the - underscore-separated, all upper case convention (for example, - <code><b>FOO_BAR</b></code>). For this case we have to use - separate expressions to handle one-word, two-word, etc., - names:</p> - - <p><code><b>--table-regex '/([A-z][a-z]+)/\U$1/'</b></code></p> - <p><code><b>--table-regex '/([A-z][a-z]+)([A-z][a-z]+)/\U$1_$2/'</b></code></p> - - <p>See also the REGEX AND SHELL QUOTING section below.</p> - - <h1>REGEX AND SHELL QUOTING</h1> - - <p>When entering a regular expression argument in the shell - command line it is often necessary to use quoting (enclosing - the argument in <code><b>" "</b></code> or - <code><b>' '</b></code>) in order to prevent the shell - from interpreting certain characters, for example, spaces as - argument separators and <code><b>$</b></code> as variable - expansions.</p> - - <p>Unfortunately it is hard to achieve this in a manner that is - portable across POSIX shells, such as those found on - GNU/Linux and UNIX, and Windows shell. For example, if you - use <code><b>" "</b></code> for quoting you will get a - wrong result with POSIX shells if your expression contains - <code><b>$</b></code>. The standard way of dealing with this - on POSIX systems is to use <code><b>' '</b></code> instead. - Unfortunately, Windows shell does not remove <code><b>' '</b></code> - from arguments when they are passed to applications. As a result you - may have to use <code><b>' '</b></code> for POSIX and - <code><b>" "</b></code> for Windows (<code><b>$</b></code> is - not treated as a special character on Windows).</p> - - <p>Alternatively, you can save regular expression options into - a file, one option per line, and use this file with the - <code><b>--options-file</b></code> option. With this approach - you don't need to worry about shell quoting.</p> - - <h1>DIAGNOSTICS</h1> - - <p>If the input file is not valid C++, <code><b>odb</b></code> - will issue diagnostic messages to STDERR and exit with non-zero exit - code.</p> - - <h1>BUGS</h1> - - <p>Send bug reports to the - <a href="mailto:odb-users@codesynthesis.com">odb-users@codesynthesis.com</a> mailing list.</p> - - </div> - <div id="footer"> - Copyright © 2009-2023 Code Synthesis Tools CC. - - <div id="terms"> - Permission is granted to copy, distribute and/or modify this - document under the terms of the - <a href="http://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. - </div> - </div> -</div> -</body> -</html> |